The viewer
ReportViewer generates a report from a template and data and shows it with navigation, zoom, print and export.
| Prop | Description |
|---|---|
template |
Template object or JSON text. |
data |
JSON data. |
document |
A ready layout document to show instead of generating one. |
params |
Parameter values, available as params.<name>. |
locale |
Locale for numbers and dates. Overrides the template’s locale. |
functions |
Extra functions callable from expressions. |
measurer |
Text measurer, see Fonts. |
maxPages, maxSteps |
Safety limits (default 10 000 pages). |
pdf, excel |
Export options. |
toolbar |
Show the toolbar (default true). |
exports |
{ pdf, excel, print } booleans. |
showIssues |
Open the issues panel initially. |
viewMode |
continuous (default) or single. |
zoom, zoomMode |
Zoom factor, and custom, fitWidth or fitPage. |
uiLocale, labels |
User interface language and label overrides. |
theme |
Colors, radius and font. |
controller |
An external ViewerController. |
allowImageSrc |
Decides which image sources the viewer may load (default: http(s), blob, raster data URIs, relative URLs). |
save |
Replaces the browser download for exports. |
injectStyles |
Add the default stylesheet (default true). |
onError / @error |
Generation or export error. |
onGenerated / @generated |
A new layout document is ready. |
All pages are shown in one scrollable column. Only the pages near the visible area are rendered, so long reports stay fast, and the page number follows the scroll position. The toolbar’s page buttons and the keys PageUp, PageDown, Home and End scroll to a page. viewMode="single" shows one page at a time.
import { ReportViewer } from '@ariana-reports/react';import { productsData, productsTemplate } from './report-data';
export default function ViewerBasic() { return <ReportViewer template={productsTemplate} data={productsData} zoomMode="fitWidth" />;}<script setup lang="ts">import { ReportViewer } from '@ariana-reports/vue';import { productsData, productsTemplate } from './report-data';</script>
<template> <ReportViewer :template="productsTemplate" :data="productsData" zoom-mode="fitWidth" /></template>{ "schemaVersion": 1, "name": "Product catalog", "unit": "mm", "page": { "size": "A4", "orientation": "portrait", "margins": { "top": 12, "right": 12, "bottom": 12, "left": 12 } }, "dataSources": [ { "name": "products" } ], "styles": { "title": { "fontSize": 18, "fontWeight": "bold", "color": "#0f172a" }, "muted": { "fontSize": 9, "color": "#64748b" }, "th": { "fontSize": 9, "fontWeight": "bold", "color": "#ffffff", "backgroundColor": "#334155", "padding": 1.5 }, "td": { "fontSize": 9, "padding": 1.5, "border": { "width": 0.4, "color": "#e2e8f0", "style": "solid", "sides": [ "bottom" ] } }, "num": { "textAlign": "right" }, "sub": { "fontSize": 9, "fontWeight": "bold", "backgroundColor": "#e2e8f0", "padding": 1.5 } }, "reportHeader": { "id": "rh", "height": 18, "components": [ { "id": "title", "type": "text", "x": 0, "y": 0, "width": 186, "height": 9, "text": "Product catalog", "styleName": "title" }, { "id": "sub", "type": "text", "x": 0, "y": 10, "width": 186, "height": 5, "text": "{{ count(products) }} products · printed {{ formatDate(today(), \"d MMMM yyyy\") }}", "styleName": "muted" } ] }, "pageFooter": { "id": "pf", "height": 8, "components": [ { "id": "pf_n", "type": "text", "x": 0, "y": 2, "width": 186, "height": 5, "text": "Page {{ page.number }} of {{ page.count }}", "style": { "fontSize": 8, "color": "#64748b", "textAlign": "right" } } ] }, "body": [ { "kind": "dataRegion", "id": "list", "dataSource": "products", "repeatHeaderOnEachPage": true, "header": { "id": "head", "height": 7, "components": [ { "id": "h1", "type": "text", "x": 0, "y": 0, "width": 28, "height": 7, "text": "SKU", "styleName": "th" }, { "id": "h2", "type": "text", "x": 28, "y": 0, "width": 72, "height": 7, "text": "Product", "styleName": "th" }, { "id": "h3", "type": "text", "x": 100, "y": 0, "width": 34, "height": 7, "text": "Category", "styleName": "th" }, { "id": "h4", "type": "text", "x": 134, "y": 0, "width": 26, "height": 7, "text": "Price", "styleName": "th", "style": { "textAlign": "right" } }, { "id": "h5", "type": "text", "x": 160, "y": 0, "width": 26, "height": 7, "text": "In stock", "styleName": "th", "style": { "textAlign": "right" } } ] }, "detail": { "id": "row", "height": 6.5, "components": [ { "id": "c1", "type": "text", "x": 0, "y": 0, "width": 28, "height": 6.5, "text": "{{ row.sku }}", "styleName": "td" }, { "id": "c2", "type": "text", "x": 28, "y": 0, "width": 72, "height": 6.5, "text": "{{ row.name }}", "styleName": "td" }, { "id": "c3", "type": "text", "x": 100, "y": 0, "width": 34, "height": 6.5, "text": "{{ row.category }}", "styleName": "td" }, { "id": "c4", "type": "text", "x": 134, "y": 0, "width": 26, "height": 6.5, "text": "{{ row.price }}", "styleName": "td", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } }, { "id": "c5", "type": "text", "x": 160, "y": 0, "width": 26, "height": 6.5, "text": "{{ row.stock }}", "styleName": "td", "style": { "textAlign": "right" }, "conditions": [ { "expression": "row.stock < 10", "style": { "color": "#b91c1c", "fontWeight": "bold" } } ] } ] }, "footer": { "id": "foot", "height": 8, "components": [ { "id": "f1", "type": "text", "x": 0, "y": 1.5, "width": 134, "height": 6, "text": "Total stock value", "styleName": "sub" }, { "id": "f2", "type": "text", "x": 134, "y": 1.5, "width": 52, "height": 6, "text": "{{ sum(rows, r => r.price * r.stock) }}", "styleName": "sub", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] } } ]}{ "products": [ { "sku": "SKU-1000", "name": "Wireless mouse 1", "category": "Hardware", "price": 12, "stock": 0 }, { "sku": "SKU-1007", "name": "Mechanical keyboard 1", "category": "Software", "price": 49.99, "stock": 13 }, { "sku": "SKU-1014", "name": "USB-C dock 1", "category": "Services", "price": 87.98, "stock": 26 }, { "sku": "SKU-1021", "name": "Webcam HD 1", "category": "Accessories", "price": 123, "stock": 39 }, { "sku": "SKU-1028", "name": "Monitor stand 1", "category": "Hardware", "price": 160.99, "stock": 52 }, { "sku": "SKU-1035", "name": "Laptop sleeve 1", "category": "Software", "price": 18.98, "stock": 65 } ]}Your own toolbar
Section titled “Your own toolbar”Every viewer is driven by a ViewerController. Hide the toolbar and use the controller, with the useViewer hook (React) or composable (Vue):
import { ReportViewer, useViewer } from '@ariana-reports/react';import { productsData, productsTemplate } from './report-data';
// Hide the built-in toolbar and drive the viewer from your own UI through its controller.export default function ViewerController() { const { controller, state } = useViewer(); return ( <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <div style={{ display: 'flex', gap: 8, alignItems: 'center', padding: 8, background: '#fff', font: '13px system-ui' }}> <button onClick={() => controller.previousPage()}>Previous</button> <span> Page {state.pageIndex + 1} of {state.pageCount} </span> <button onClick={() => controller.nextPage()}>Next</button> <button onClick={() => controller.zoomIn()}>Zoom in</button> <button onClick={() => controller.zoomOut()}>Zoom out</button> <button onClick={() => controller.downloadPdf()} disabled={state.busy}> Download PDF </button> </div> <div style={{ flex: 1, minHeight: 0 }}> <ReportViewer controller={controller} template={productsTemplate} data={productsData} toolbar={false} zoomMode="fitWidth" /> </div> </div> );}<script setup lang="ts">import { ReportViewer, useViewer } from '@ariana-reports/vue';import { productsData, productsTemplate } from './report-data';
// Hide the built-in toolbar and drive the viewer from your own UI through its controller.const { controller, state } = useViewer();</script>
<template> <div style="display: flex; flex-direction: column; height: 100%"> <div style="display: flex; gap: 8px; align-items: center; padding: 8px; background: #fff; font: 13px system-ui"> <button @click="controller.previousPage()">Previous</button> <span>Page {{ state.pageIndex + 1 }} of {{ state.pageCount }}</span> <button @click="controller.nextPage()">Next</button> <button @click="controller.zoomIn()">Zoom in</button> <button @click="controller.zoomOut()">Zoom out</button> <button :disabled="state.busy" @click="controller.downloadPdf()">Download PDF</button> </div> <div style="flex: 1; min-height: 0"> <ReportViewer :controller="controller" :template="productsTemplate" :data="productsData" :toolbar="false" zoom-mode="fitWidth" /> </div> </div></template>{ "schemaVersion": 1, "name": "Product catalog", "unit": "mm", "page": { "size": "A4", "orientation": "portrait", "margins": { "top": 12, "right": 12, "bottom": 12, "left": 12 } }, "dataSources": [ { "name": "products" } ], "styles": { "title": { "fontSize": 18, "fontWeight": "bold", "color": "#0f172a" }, "muted": { "fontSize": 9, "color": "#64748b" }, "th": { "fontSize": 9, "fontWeight": "bold", "color": "#ffffff", "backgroundColor": "#334155", "padding": 1.5 }, "td": { "fontSize": 9, "padding": 1.5, "border": { "width": 0.4, "color": "#e2e8f0", "style": "solid", "sides": [ "bottom" ] } }, "num": { "textAlign": "right" }, "sub": { "fontSize": 9, "fontWeight": "bold", "backgroundColor": "#e2e8f0", "padding": 1.5 } }, "reportHeader": { "id": "rh", "height": 18, "components": [ { "id": "title", "type": "text", "x": 0, "y": 0, "width": 186, "height": 9, "text": "Product catalog", "styleName": "title" }, { "id": "sub", "type": "text", "x": 0, "y": 10, "width": 186, "height": 5, "text": "{{ count(products) }} products · printed {{ formatDate(today(), \"d MMMM yyyy\") }}", "styleName": "muted" } ] }, "pageFooter": { "id": "pf", "height": 8, "components": [ { "id": "pf_n", "type": "text", "x": 0, "y": 2, "width": 186, "height": 5, "text": "Page {{ page.number }} of {{ page.count }}", "style": { "fontSize": 8, "color": "#64748b", "textAlign": "right" } } ] }, "body": [ { "kind": "dataRegion", "id": "list", "dataSource": "products", "repeatHeaderOnEachPage": true, "header": { "id": "head", "height": 7, "components": [ { "id": "h1", "type": "text", "x": 0, "y": 0, "width": 28, "height": 7, "text": "SKU", "styleName": "th" }, { "id": "h2", "type": "text", "x": 28, "y": 0, "width": 72, "height": 7, "text": "Product", "styleName": "th" }, { "id": "h3", "type": "text", "x": 100, "y": 0, "width": 34, "height": 7, "text": "Category", "styleName": "th" }, { "id": "h4", "type": "text", "x": 134, "y": 0, "width": 26, "height": 7, "text": "Price", "styleName": "th", "style": { "textAlign": "right" } }, { "id": "h5", "type": "text", "x": 160, "y": 0, "width": 26, "height": 7, "text": "In stock", "styleName": "th", "style": { "textAlign": "right" } } ] }, "detail": { "id": "row", "height": 6.5, "components": [ { "id": "c1", "type": "text", "x": 0, "y": 0, "width": 28, "height": 6.5, "text": "{{ row.sku }}", "styleName": "td" }, { "id": "c2", "type": "text", "x": 28, "y": 0, "width": 72, "height": 6.5, "text": "{{ row.name }}", "styleName": "td" }, { "id": "c3", "type": "text", "x": 100, "y": 0, "width": 34, "height": 6.5, "text": "{{ row.category }}", "styleName": "td" }, { "id": "c4", "type": "text", "x": 134, "y": 0, "width": 26, "height": 6.5, "text": "{{ row.price }}", "styleName": "td", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } }, { "id": "c5", "type": "text", "x": 160, "y": 0, "width": 26, "height": 6.5, "text": "{{ row.stock }}", "styleName": "td", "style": { "textAlign": "right" }, "conditions": [ { "expression": "row.stock < 10", "style": { "color": "#b91c1c", "fontWeight": "bold" } } ] } ] }, "footer": { "id": "foot", "height": 8, "components": [ { "id": "f1", "type": "text", "x": 0, "y": 1.5, "width": 134, "height": 6, "text": "Total stock value", "styleName": "sub" }, { "id": "f2", "type": "text", "x": 134, "y": 1.5, "width": 52, "height": 6, "text": "{{ sum(rows, r => r.price * r.stock) }}", "styleName": "sub", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] } } ]}{ "products": [ { "sku": "SKU-1000", "name": "Wireless mouse 1", "category": "Hardware", "price": 12, "stock": 0 }, { "sku": "SKU-1007", "name": "Mechanical keyboard 1", "category": "Software", "price": 49.99, "stock": 13 }, { "sku": "SKU-1014", "name": "USB-C dock 1", "category": "Services", "price": 87.98, "stock": 26 }, { "sku": "SKU-1021", "name": "Webcam HD 1", "category": "Accessories", "price": 123, "stock": 39 }, { "sku": "SKU-1028", "name": "Monitor stand 1", "category": "Hardware", "price": 160.99, "stock": 52 }, { "sku": "SKU-1035", "name": "Laptop sleeve 1", "category": "Software", "price": 18.98, "stock": 65 } ]}Useful members: load(template, data), goToPage(index), nextPage(), previousPage(), setZoom(1.25), setZoomMode('fitWidth'), downloadPdf(), downloadExcel(), print(), regenerate(), getState() and subscribe(listener). The state holds document, error, pageIndex, pageCount, zoom, zoomMode and busy.
Without a framework
Section titled “Without a framework”The engine and the renderer have no framework dependency:
import { PageList, generateReport, renderDocument } from '@ariana-reports/core';
const doc = generateReport(template, data, { params: { title: 'Q1' } });
// All pages at once:renderDocument(doc, document.getElementById('report')!);
// Or a continuous, lazily rendered view with page tracking:const scroller = document.getElementById('scroller')!;const host = document.getElementById('pages')!;const list = new PageList(scroller, host, { onPageChange: (index) => console.log('page', index + 1) });list.update({ document: doc, zoom: 1, pageIndex: 0 });Issues (failed expressions, clipped bands) are in doc.issues.
Keyboard
Section titled “Keyboard”With the page area focused: PageDown / PageUp move between pages, Home / End go to the first / last page.