The designer
ReportDesigner is a banded design surface with a toolbox, a data panel, a properties panel and a live preview.
| Prop | Description |
|---|---|
template |
The starting template (object or JSON text). Later changes to the prop are ignored; use the controller to load another template. |
sampleData |
JSON data used to detect fields and for the preview tab. |
onChange / @change |
Called after every edit with the new template. |
onSave / :on-save |
Called when the user clicks Save, with (template, json). May return a promise. Without it, Save downloads a .json file. |
settings |
Initial grid, snapping and margin settings: grid, showGrid, snapToGrid, snapToObjects, snapTolerance, showMargins. |
fonts |
Extra font families for the font lists, see Fonts. |
preview |
Options for the preview tab: params, measurer, pdf, excel. |
uiLocale, labels |
User interface language and label overrides. |
theme |
Colors, radius and font. |
controller |
An external DesignerController. |
injectStyles |
Add the default stylesheet (default true). |
Working on the surface
Section titled “Working on the surface”- Select by clicking, with Shift or Ctrl to add more, or by dragging a rubber band in a band, in the empty strip under a band, or in the page margin. Ctrl/Cmd+A selects everything in the band.
- Move by dragging, or with the arrow keys (Shift or Alt for fine steps). Dragging over another band moves the components into it. Alt while dragging turns snapping off.
- Resize with the eight handles on the edge of every selected component. With several components selected, one handle resizes all of them by the same amount.
- Resize a band by dragging its bottom edge.
- Edit text in the properties panel or, with a double click, in a large editor with formatting buttons and a searchable list of fields, variables and functions. Ctrl+Enter applies; Esc cancels.
- Alignment buttons in the toolbar align the content of the selected text boxes (left, center, right, justify, top, middle, bottom) and show the current state. Use distribute, bring to front and send to back for box positions.
- Band colors: each band’s caption uses a dark color and its area a light tint. Change it in the band’s properties; it only affects the designer.
- Margins are drawn as a dashed frame. The Margins, Grid and Snap buttons toggle the guides.
Keyboard shortcuts
Section titled “Keyboard shortcuts”| Keys | Action |
|---|---|
| Arrows | Move the selection (Shift or Alt: fine steps) |
| Delete | Remove the selection |
| Ctrl/Cmd + Z, Shift + Z, Y | Undo, redo |
| Ctrl/Cmd + C, X, V, D | Copy, cut, paste, duplicate |
| Ctrl/Cmd + A | Select the band’s components |
| Escape | Cancel a drag, or clear the selection |
The controller
Section titled “The controller”The designer is a view over a framework-neutral DesignerController. Create one yourself to read or change the template from your own UI:
import { ReportDesigner, useDesigner } from '@ariana-reports/react';
function Editor({ template }) { const { controller, state } = useDesigner({ template }); return ( <> <button disabled={!state.canUndo} onClick={() => controller.undo()}>Undo</button> <span>{state.dirty ? 'Unsaved changes' : 'Saved'}</span> <ReportDesigner controller={controller} /> </> );}<script setup lang="ts">import { ReportDesigner, useDesigner } from '@ariana-reports/vue';
const props = defineProps<{ template: object }>();const { controller, state } = useDesigner({ template: props.template as never });</script>
<template> <button :disabled="!state.canUndo" @click="controller.undo()">Undo</button> <span>{{ state.dirty ? 'Unsaved changes' : 'Saved' }}</span> <ReportDesigner :controller="controller" /></template>Useful members: template, toJSON(), load(template), markSaved(), undo(), redo(), select(ids), addComponent(bandId, type), insertField(bandId, path), moveComponents, resizeComponents, setTextAlign, addStaticBand(), addDataRegion(source), addGroup(regionId, expression), addChildRegion, setPageBand, updateTemplate, inferDataSourceFields(name), preview() and subscribe(listener). The state has the template, selection, settings, canUndo, canRedo, dirty and errors. Every edit makes a new template object, so you can compare by reference.
Try it
Section titled “Try it”import { useState } from 'react';import { ReportDesigner, type ReportTemplate } from '@ariana-reports/react';import { salesData, salesTemplate } from './report-data';
export default function DesignerBasic() { const [template, setTemplate] = useState<ReportTemplate>(salesTemplate); return ( <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <div style={{ flex: 1, minHeight: 0 }}> {/* The template prop is only the starting point; changes are reported through onChange. */} <ReportDesigner template={salesTemplate} sampleData={salesData} onChange={setTemplate} onSave={async (_template, json) => { // Send `json` to your backend here. console.log('save', json.length, 'bytes'); }} /> </div> <div style={{ padding: '4px 10px', background: '#fff', font: '12px system-ui', color: '#555' }}> Template JSON: {JSON.stringify(template).length} characters </div> </div> );}<script setup lang="ts">import { ref } from 'vue';import { ReportDesigner, type ReportTemplate } from '@ariana-reports/vue';import { salesData, salesTemplate } from './report-data';
const template = ref<ReportTemplate>(salesTemplate);
async function save(_template: ReportTemplate, json: string) { // Send `json` to your backend here. console.log('save', json.length, 'bytes');}</script>
<template> <div style="display: flex; flex-direction: column; height: 100%"> <div style="flex: 1; min-height: 0"> <!-- The template prop is only the starting point; changes are reported through the change event. --> <ReportDesigner :template="salesTemplate" :sample-data="salesData" :on-save="save" @change="template = $event" /> </div> <div style="padding: 4px 10px; background: #fff; font: 12px system-ui; color: #555"> Template JSON: {{ JSON.stringify(template).length }} characters </div> </div></template>{ "schemaVersion": 1, "name": "Sales by customer", "unit": "mm", "page": { "size": "A4", "orientation": "portrait", "margins": { "top": 12, "right": 12, "bottom": 12, "left": 12 } }, "dataSources": [ { "name": "orders" } ], "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": 16, "components": [ { "id": "title", "type": "text", "x": 0, "y": 0, "width": 186, "height": 9, "text": "Sales by customer", "styleName": "title" }, { "id": "sub", "type": "text", "x": 0, "y": 10, "width": 186, "height": 5, "text": "{{ count(orders) }} orders from {{ distinct(orders, \"customer\").length }} customers", "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": "orders_region", "dataSource": "orders", "sort": [ { "expression": "row.date", "direction": "asc" } ], "groups": [ { "id": "by_customer", "expression": "row.customer", "header": { "id": "gh", "height": 8, "components": [ { "id": "gh1", "type": "text", "x": 0, "y": 1, "width": 186, "height": 6, "text": "{{ group.key }}", "style": { "fontSize": 11, "fontWeight": "bold", "color": "#1d4ed8", "padding": 1 } } ] }, "footer": { "id": "gf", "height": 8, "components": [ { "id": "gf1", "type": "text", "x": 0, "y": 1, "width": 134, "height": 6, "text": "Subtotal ({{ group.count }} orders)", "styleName": "sub" }, { "id": "gf2", "type": "text", "x": 134, "y": 1, "width": 52, "height": 6, "text": "{{ sum(group.rows, \"amount\") }}", "styleName": "sub", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] } } ], "header": { "id": "head", "height": 7, "components": [ { "id": "h1", "type": "text", "x": 0, "y": 0, "width": 22, "height": 7, "text": "Order", "styleName": "th" }, { "id": "h2", "type": "text", "x": 22, "y": 0, "width": 30, "height": 7, "text": "Date", "styleName": "th" }, { "id": "h3", "type": "text", "x": 52, "y": 0, "width": 82, "height": 7, "text": "Product", "styleName": "th" }, { "id": "h4", "type": "text", "x": 134, "y": 0, "width": 52, "height": 7, "text": "Amount", "styleName": "th", "style": { "textAlign": "right" } } ] }, "detail": { "id": "row", "height": 6.5, "components": [ { "id": "c1", "type": "text", "x": 0, "y": 0, "width": 22, "height": 6.5, "text": "#{{ row.id }}", "styleName": "td" }, { "id": "c2", "type": "text", "x": 22, "y": 0, "width": 30, "height": 6.5, "text": "{{ row.date }}", "styleName": "td", "format": { "type": "date", "pattern": "dd MMM yyyy" } }, { "id": "c3", "type": "text", "x": 52, "y": 0, "width": 82, "height": 6.5, "text": "{{ row.product }}", "styleName": "td" }, { "id": "c4", "type": "text", "x": 134, "y": 0, "width": 52, "height": 6.5, "text": "{{ row.amount }}", "styleName": "td", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] }, "footer": { "id": "foot", "height": 10, "components": [ { "id": "f1", "type": "text", "x": 0, "y": 2, "width": 134, "height": 7, "text": "Grand total", "style": { "fontSize": 11, "fontWeight": "bold", "backgroundColor": "#1e293b", "color": "#fff", "padding": 1.5 } }, { "id": "f2", "type": "text", "x": 134, "y": 2, "width": 52, "height": 7, "text": "{{ sum(rows, \"amount\") }}", "style": { "fontSize": 11, "fontWeight": "bold", "backgroundColor": "#1e293b", "color": "#fff", "padding": 1.5, "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] } } ]}{ "orders": [ { "id": 1001, "customer": "Contoso Ltd", "date": "2026-01-08", "product": "Cloud backup", "amount": 480 }, { "id": 1002, "customer": "Fabrikam", "date": "2026-01-11", "product": "USB-C dock", "amount": 129.9 }, { "id": 1003, "customer": "Contoso Ltd", "date": "2026-01-15", "product": "Support plan", "amount": 1200 }, { "id": 1004, "customer": "Northwind", "date": "2026-01-19", "product": "Monitor stand", "amount": 54.5 }, { "id": 1005, "customer": "Fabrikam", "date": "2026-02-02", "product": "Headset", "amount": 89 }, { "id": 1006, "customer": "Northwind", "date": "2026-02-05", "product": "Webcam HD", "amount": 75.25 } ]}