Visualizations reference
@astrato-io/visualizations renders Astrato’s governed charts in any web page. It pairs with an
@astrato-io/sdk client, which supplies data, metadata and selections. An externally hosted app
creates that client directly; a Data App obtains it from @astrato-io/app-runtime.
Use Getting started with Data Apps to choose the app model before configuring the client. The examples below
assume that client and dataBinding were obtained through the selected route.
Package details:
- Name:
@astrato-io/visualizations. - ESM only.
- Browser only. Every JavaScript entry point requires browser globals at import time, including
/definitions. - Install from npmjs.
In the semantic editor, a loading indicator appears while a chart surface opens for the first time. A preview reports a rendering error if its first render fails or reaches the runtime’s readiness deadline.
Create and update visualizations
Section titled “Create and update visualizations”Do not build or edit a vSpec directly. Use one of the package’s two supported APIs:
- Definitions create a chart: a declarative description compiles into a fresh vSpec.
- Operations update an existing chart through named changes with documented payload schemas.
Read the definition reference for creation requirements and examples, and the operations reference for every operation and its complete payload schema, grouped by visualization type. Both are generated from package metadata; each page identifies its package version.
You can also read the references from your installed package without importing JavaScript or loading a chart. Use the bundled reference when your installed version differs from this site:
node_modules/@astrato-io/visualizations/VISUALIZATION_DEFINITIONS.mdnode_modules/@astrato-io/visualizations/docs/operations/index.md
Install
Section titled “Install”Check package versions before development, including when reusing an existing installation.
Externally hosted app:
npm install @astrato-io/visualizations @astrato-io/sdkData App:
npm install @astrato-io/visualizations @astrato-io/app-runtimeSubpath exports:
| Package entry | Contents |
|---|---|
@astrato-io/visualizations |
Registry, runtime, web components. |
@astrato-io/visualizations/definitions |
Create charts. Browser-only. |
@astrato-io/visualizations/operations |
Modify charts: applyOperations, applyRuntimeOperations. |
@astrato-io/visualizations/getters |
Read chart state: processGetterRequests, processRuntimeGetterRequests. |
@astrato-io/visualizations/metadata |
Discovery: getVisualizationMetadata, visualizationMetadata. |
@astrato-io/visualizations/styles.css |
Package CSS. Only the date-picker object needs it, but always import it. |
@astrato-io/visualizations/VISUALIZATION_DEFINITIONS.md |
Readable creation reference. |
@astrato-io/visualizations/docs/operations/* |
Readable operation references with full payload schemas. |
Mountable registry types
Section titled “Mountable registry types”import { registry, listVisualizationRegistryEntries, getVisualizationRegistryEntry, createDefaultVSpec,} from '@astrato-io/visualizations';Type IDs are case-sensitive. In particular, keep the existing capitalization of heatMap and
simpleTextObject.
- Charts:
bar,combo,distribution-gauge,dot,gauge,heatMap,histogram,line,pie,radar,sankey,scatter,table,tree,treemap,waterfall,word-cloud - Filters:
date-picker,filter,slider - Objects:
iframe,image,number,shape,simpleTextObject
getVisualizationRegistryEntry(type) throws Unsupported standalone visualization type: <type>
for unknown IDs. createDefaultVSpec(type) returns the fresh, empty vSpec required when compiling
a definition.
Each registry entry includes capabilities:
{ supportsSelections: boolean; supportsStaticFilters: boolean; supportsOperations: boolean; supportsGetters: boolean;}Every mountable type supports operations. date-picker, slider, and simpleTextObject do not
support getters. iframe, image, number, shape, and simpleTextObject do not support
selections.
Use runtime discovery instead of hard-coding operation lists:
import { getVisualizationMetadata } from '@astrato-io/visualizations/metadata';
const meta = getVisualizationMetadata('bar');meta.operations.names; // every operation name valid for 'bar'meta.operations.items; // { name, friendlyName?, description?, payloadSchema? }[]meta.schemas.vSpec; // JSON Schema of the bar vSpecmeta.requirements; // data requirementsmeta.examples;Definitions: create a chart
Section titled “Definitions: create a chart”import { compileVisualizationDefinition, createVisualizationAvailableFields, listVisualizationDefinitionDescriptors, validateVisualizationDefinition,} from '@astrato-io/visualizations/definitions';A definition is a declarative JSON object. The compiler applies it to a fresh base vSpec and validates the result before returning it.
Available fields
Section titled “Available fields”The compiler resolves your fieldKey references against a field catalog:
const metadata = await client.services.engine.fields.getFieldMetadata?.({ ...dataBinding });const availableFields = createVisualizationAvailableFields(metadata ?? {});Field keys are opaque engine identifiers, for example [Cars].[Manufacturer]. Use them
exactly as the metadata returns them. Never parse them or rebuild them from names.
const compilation = await compileVisualizationDefinition({ definition: { version: 1, // supported definition version type: 'bar', data: { dimensions: [{ ref: 'manufacturer', fieldKey: '[Cars].[Manufacturer]' }], measures: [{ ref: 'sales', fieldKey: '[Cars].[Sales In Thousands]', aggregation: 'SUM' }], }, sorting: [{ by: 'sales', direction: 'desc' }], }, availableFields, baseVSpec: createDefaultVSpec('bar'), // must be fresh; this factory always is});
if (!compilation.success) { // compilation.issues: [{ code, message, path, details?, allowedValues? }] throw new Error(JSON.stringify(compilation.issues, null, 2));}
compilation.vSpec; // the compiled chart specificationcompilation.resolvedRefs; // ref -> created data item keys. Transient: never persist it.Behavior and constraints:
- Definitions exist for every mountable type and for
atomic-input. Do not useatomic-input: it has no registry entry,createDefaultVSpec('atomic-input')throws, and it cannot be mounted. - The result is a discriminated union on
success. Failures return issue codes includingnon_fresh_base_vspec(your base vSpec already had data — usecreateDefaultVSpec),unknown_field_key,requirements_not_met, andoperation_failed. - Definitions initialize charts. They cannot re-shape an existing chart — a base vSpec with any data in it is rejected. To change a chart, use operations.
- Each type has a descriptor with a zod schema, a generated JSON Schema, documentation
(summary, data requirements, constraints), and canonical examples:
getVisualizationDefinitionDescriptor(type)/listVisualizationDefinitionDescriptors(). validateVisualizationDefinition(definition)checks the schema without compiling.- The definition reference is a generated per-type reference with
requirements, constraints, and a canonical example for each type. Read it from
node_modules/@astrato-io/visualizations/VISUALIZATION_DEFINITIONS.md.
Value formats
Section titled “Value formats”format on a dimension, measure or table column replaces the format that item would otherwise use,
so set it only when you want a different one. Reference examples set one to show the shape; do not
copy it by default.
- Stored measures and dimensions. Leave
formatoff to keep the format the semantic layer sets, including its currency. AnyformatTypereplaces it. - Dates. Leave
formatoff a date dimension or column to show a date, or set a date format, such as{ formatType: 'custom', formatString: 'MMM yyyy' }.nonedraws the raw epoch number, and so doesinheritunless the field has a format of its own. - Currency. A money format adds no symbol by itself. Set
currency, as in{ formatType: 'money2Decimal', currency: '$' }, where the layer or the reader states the currency, and set the same format on every object that shows the value: a KPI, a chart and a table column. Where neither states one, show a plain number rather than guess.
Static filters
Section titled “Static filters”The operator of a filterGroup filter decides which key holds its value.
| Operator | Value |
|---|---|
equals, doesNotEqual, greaterThan, lessThan, greaterThanOrEqualTo, lessThanOrEqualTo, contains, excludes, like, notLike, startsWith, endsWith |
value, one string |
inList, excludedFromList |
values, an array of strings |
isBetween, isNotBetween |
valueFrom and valueTo, two strings |
isNull, notNull |
None |
const filterGroup = { operator: 'and', filters: [ { fieldKey: stateKey, operator: 'contains', value: 'TX' }, { fieldKey: channelKey, operator: 'inList', values: ['Direct', 'Online'] }, ],};Operations: modify a chart
Section titled “Operations: modify a chart”import { applyOperations, applyRuntimeOperations } from '@astrato-io/visualizations/operations';An operation request is { id?, name, payload }. Requests run sequentially; each sees the previous
result. Set id whenever you need to inspect a request’s result. If you omit it, the package
generates an opaque ID. The returned results object is keyed by the IDs actually used.
await applyRuntimeOperations({ runtime, instanceId: 'sales-by-manufacturer', availableFields, operationRequests: [{ id: 'flip', name: 'setBarChartOrientation', payload: 'horizontal' }],});applyRuntimeOperationstargets a runtime-owned instance and serializes batches per instance. It updates the stored vSpec whether or not the instance is mounted; a mounted chart updates in place.applyOperations({ instance, operationRequests, ... })is the lower-level variant. It returns{ vSpec, results }without updating a runtime instance.- Unknown operation names throw
Unsupported operation for <type>: <name>. - Batches are not transactional. One request’s result can report
success: falsewhile later requests still run. - Each operation has a 1-second execution timeout.
Validate your own payloads
Section titled “Validate your own payloads”Operation payloads are not validated at apply time. Each operation declares a
payloadSchema (JSON Schema) in its manifest, but nothing enforces it when you call. A
malformed payload does not reliably fail — it can silently produce a wrong vSpec.
Before calling an operation:
- Read its schema in the operations reference for your visualization
type, or obtain
payloadSchemafromgetVisualizationMetadata(type).operations.itemsin the browser. - Validate your payload against that schema yourself.
Finding operations
Section titled “Finding operations”Operations and getters are type-specific. Start with the operations reference to look up operation names and payloads. It covers the full standalone catalog, including operations outside Astrato’s smaller Actions menu. A listed operation may still require particular chart state or fields, as described on its reference page.
For programmatic discovery in the browser, read the selected type’s runtime metadata:
import { getVisualizationMetadata } from '@astrato-io/visualizations/metadata';
const barOps = getVisualizationMetadata('bar').operations;barOps.count;barOps.names;barOps.items.find((op) => op.name === 'setBarChartOrientation')?.payloadSchema;Axis ranges and scales
Section titled “Axis ranges and scales”In 0.6.2, bar, combo, dot, histogram, line, scatter and waterfall support fixed
measure-axis bounds and an automatic range anchored at zero. Set the initial range in
presentation.axes in the definition. For a mounted chart, use
setAxisRangeOption,
setAxisRangeMin,
setAxisRangeMax and
setAxisStartsAtZero.
Use identical bounds on charts that must share a scale. Check the type’s reference for its axis names.
bar, combo and line also support linear and logarithmic measure axes through the definition
or setAxisScale. A logarithmic axis draws zero
and negative values at its floor.
bar, combo, histogram, line and waterfall can shade fixed ranges on the primary measure
axis. Use presentation.axes.primaryMeasure.bands in a definition or
setMeasureAxisBands on a mounted chart.
These bands span two measure values; they are not uncertainty intervals that follow a series.
Line-end labels
Section titled “Line-end labels”Use setShowEndLabels to label each series
at the end of a vertical, unstacked line chart. Stacked and horizontal line charts retain the
setting but draw no end labels. The position and backdrop have separate operations in the
line reference.
Chart animations
Section titled “Chart animations”For types that expose setAnimationEnabled,
send { enabled: false } to disable animations on initial rendering and data updates.
To honour reduced motion, your app must read prefers-reduced-motion and apply that choice;
the chart does not read the media query itself. A host can also disable animations.
Rendering
Section titled “Rendering”Render visualizations through a runtime, either with the <astrato-visualization> web component or
by mounting directly into a DOM element. The package does not include a React component.
The runtime
Section titled “The runtime”For a Data App, obtain both values from the Data App runtime. Do not configure credentials or IDs:
import { createAstratoApp } from '@astrato-io/app-runtime';
const app = await createAstratoApp();const client = app.client;const dataBinding = app.dataBinding;For an externally hosted app, configure the client and binding through the app’s own trusted authentication and deployment boundary:
import { createAstratoClient } from '@astrato-io/sdk';
const client = createAstratoClient({ baseUrl: window.location.origin, tenantId: 'your-tenant-id', // Your same-origin backend authenticates requests; use a local proxy during development. visualizationEngine: true, // required: charts fetch data through the engine service});
const dataBinding = { tenantId: 'your-tenant-id', semanticLayerId: 'your-semantic-layer-id',};Then create one visualization runtime per application:
import { createVisualizationRuntime, registry } from '@astrato-io/visualizations';
const runtime = createVisualizationRuntime({ client, registry });Create one client and one runtime per application.
Instances describe charts:
runtime.createInstance({ id: 'sales-by-manufacturer', type: 'bar', groupId: 'dashboard', // selection group; see the Interactivity guide vSpec: compilation.vSpec, // omit to get the type's default vSpec dataBinding,});Mounting
Section titled “Mounting”To mount directly:
const container = document.getElementById('chart-slot'); // must have non-zero sizeif (!container) throw new Error('Missing #chart-slot container.');const handle = await runtime.mount(container, 'sales-by-manufacturer');const rendered = await handle.ready;
handle.status; // 'mounted' | 'error' | 'unmounted' — structural mount staterendered.status; // 'rendered' | 'error' | 'unmounted' | 'timeout'// handle.unmount() when donehandle.status reports structural mount state. handle.ready reports the first render and resolves
with { instanceId, status }, where status is rendered, error, unmounted or timeout. Check
for status === 'rendered' before treating the chart as ready; a successful mount can still end in
a render error or timeout.
Readiness has a built-in 40-second deadline.
Set readyTimeoutMs when creating the runtime to change it for both direct mounts and web components:
const runtime = createVisualizationRuntime({ client, registry, readyTimeoutMs: 60_000, // optional; defaults to 40_000});Use an integer from 1 to 2,147,483,647 milliseconds. The deadline starts when chart initialization
begins, after metadata preloading and lazy module loading; it does not limit those earlier steps of
runtime.mount().
On expiry, readiness resolves with { instanceId, status: 'timeout' }. The chart stays mounted and
may render later, but that readiness result remains timeout. Unmounting before readiness settles
resolves it as unmounted. For panel lifecycle and background-tab handling, see
Readiness.
To use the web component:
import { registerAstratoElements } from '@astrato-io/visualizations';
registerAstratoElements({ runtime }); // defines <astrato-visualization>, idempotent<astrato-visualization instance-id="sales-by-manufacturer"></astrato-visualization><style> astrato-visualization { display: block; width: 100%; height: 400px; }</style>instance-id is the element’s only attribute. Everything else — vSpec, data, group — lives on
the runtime instance.
Change a mounted visualization through its named runtime operations.
Do not call runtime.updateInstance directly or write vSpec properties to bypass an operation.
If no operation supports the requested change, it is not supported on the mounted visualization.
React note
Section titled “React note”React StrictMode runs effects twice during development, while runtime.mount is asynchronous.
Serialize mount and unmount through a promise chain to avoid mounting the same chart twice:
const lifecycleRef = useRef<Promise<void>>(Promise.resolve());
useEffect(() => { const container = containerRef.current; if (!container) return;
let disposed = false; let released = false; let handle: Awaited<ReturnType<typeof runtime.mount>> | undefined;
const release = () => { if (released) return; released = true; handle?.unmount(); if (runtime.listInstances().some(({ id }) => id === instance.id)) { runtime.removeInstance(instance.id); } };
const mount = async () => { if (disposed) return;
try { runtime.createInstance(instance); handle = await runtime.mount(container, instance.id); if (disposed) release(); } catch (error) { release(); if (!disposed) console.error('Failed to mount visualization', error); } };
lifecycleRef.current = lifecycleRef.current.catch(() => undefined).then(mount);
return () => { disposed = true; lifecycleRef.current = lifecycleRef.current.catch(() => undefined).then(release); };}, [instance, runtime]);Styling and theming
Section titled “Styling and theming”- The host page must give the container a non-zero size. A zero-size container logs a warning and renders nothing visible.
- Always
import '@astrato-io/visualizations/styles.css'. It contains date-picker styles only; charts style themselves with injected CSS. - The runtime has no host theme input. Charts start with the built-in Astrato light theme (Roboto, white background). A chart will not follow your page’s dark theme. Use appearance operations to store custom styling in the vSpec.
- Appearance operations are type-specific. Depending on the selected visualization, the metadata may
expose operations for data-label and legend font sizes, axis colours, number-object font family,
size, weight and colour, and chart palettes. The built-in theme specifies Roboto; there is no
single font-family operation for all chart text. Load chosen web fonts in the host page; setting
a font family does not download it, and unavailable fonts use the browser’s fallback.
Discover the exact names and payload schemas with
getVisualizationMetadata(type).operations.itemsbefore applying them. - Categorical charts can use
setChartPalettewith a built-in palette name, orsetCustomPaletteColorswith an ordered list of colours.setColoringPersistentColorsaccepts a boolean:falseassigns colours in domain order, whiletruehashes each value into the palette. With the same values and ordered palette, retained categories keep their colours when filtering removes or reorders other categories. Different values can share a palette slot; changing the palette’s order or length changes the mapping. This is not an explicit category-value-to-colour map.
For example, for a bar chart with a categorical dimension, select categorical colouring before applying its palette. Check the supported operations and validate their payloads as described in Operations:
const styled = await applyRuntimeOperations({ runtime, instanceId: 'sales-by-manufacturer', availableFields, operationRequests: [ { id: 'mode', name: 'setColoringType', payload: 'categorical' }, { id: 'palette', name: 'setCustomPaletteColors', payload: { colors: ['#2563eb', '#14b8a6', '#f59e0b'] } }, { id: 'persistence', name: 'setColoringPersistentColors', payload: true }, ],});
// `results` is typed `unknown`: each entry is { id, name, success, error? }.const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;if (!isRecord(styled.results)) throw new Error('Unexpected styling operation results');for (const [id, result] of Object.entries(styled.results)) { if (!isRecord(result) || result.success !== true) { throw new Error(`Styling operation ${id} failed: ${JSON.stringify(result)}`); }}For a number object, the corresponding font-family operation takes a string payload:
await applyRuntimeOperations({ runtime, instanceId: 'revenue-number', availableFields, operationRequests: [{ name: 'setNumberFontFamily', payload: 'Arial' }],});These operations modify the selected instance’s stored vSpec. Configure each chart that must share styling; the runtime has no global theme or cross-instance colour registry. For categorical colours to agree across charts, their colouring values and ordered palettes must agree too. Check the rendered assignments, including after filtering; sharing a palette alone is insufficient. Charts do not use shadow DOM, so host CSS can still leak into them.
Colouring types
Section titled “Colouring types”setChartPalette, resetChartPaletteToThemeDefault and setCustomPaletteColors are refused while a
chart is drawn in one colour, and several chart types open that way: every slice of a default pie
has the same colour. Send setColoringType first, in the same batch:
| Chart | Opens in | setColoringType before a palette |
|---|---|---|
bar with one dimension and one measure |
One colour | categorical, which colours each bar |
pie, scatter, dot, word-cloud |
One colour | categorical |
gauge with a dimension, radar with two dimensions |
One colour | categorical |
gauge without a dimension, combo, distribution-gauge |
One colour | perEachMeasure |
Any other bar, line, treemap, sankey |
A palette | None |
When setColoringType refuses a value, its error lists the types that chart accepts. Other radar
shapes stay in one colour. heatMap has no palette: send setColoringType gradient, then
setCustomGradientColors.
To keep one colour, send setColoringColor instead of a palette. A line or combo accepts it but
ignores it until setColoringType is single.
Date labels
Section titled “Date labels”Grouping a date with setDimensionTemporalGrouping picks a matching label format: a month reads
Jan 2026 and a quarter 2026 Q1. That format follows later grouping changes. To use another,
send setDimensionFormatType after the grouping, with a type that level accepts:
dateLevel |
Format types |
|---|---|
year |
yyyy, yy, ddmmyyyy3 |
quarter |
yyyyq, qyyyy |
month |
mmmyyyy, mmmmyyyy, mmyyyy, yyyymm, yyyymmm, yyyymmmm |
week |
yyyywn, wnyyyy, yywn, ddmmyyyy3 |
For a pattern these do not give, set the format type to custom first, because
setDimensionFormatString is refused under any other type:
const { dataItemInternalKey, dataItemUniqueSignature } = compilation.resolvedRefs.month;const month = { dataItemInternalKey, dataItemUniqueSignature };
await applyRuntimeOperations({ runtime, instanceId: 'monthly-revenue', availableFields, operationRequests: [ { id: 'grain', name: 'setDimensionTemporalGrouping', payload: { ...month, mode: 'level', dateLevel: 'month' } }, { id: 'type', name: 'setDimensionFormatType', payload: { ...month, formatType: 'custom' } }, { id: 'labels', name: 'setDimensionFormatString', payload: { ...month, formatString: 'mmm yy' } }, ],});Here labels read Jan 26. A custom format stays when the grouping changes, so set a new one for
each level. custom works on an ungrouped date and at the four levels above; day and date parts
refuse it. A dimension in a definition can open with a custom format; see
Value formats.
Common errors
Section titled “Common errors”| Message | Meaning |
|---|---|
Unsupported standalone visualization type: <type> |
Unknown registry type ID. Check the mountable registry list. |
Unsupported operation for <type>: <name> |
The operation does not exist for that chart type. Read getVisualizationMetadata(type).operations.names. |
Unsupported visualization host access: vBackendAPI.<path> |
The chart requested a host API that is unavailable in the standalone package. |
Astrato SDK engine service is not configured. Provide an engine service. |
Create the SDK client with visualizationEngine: true. |
Standalone visualization data service is not configured. Provide engine.getData or data.query. |
The supplied client exposes neither data API. With the Astrato SDK, enable visualizationEngine. |
Unsupported value "contains". Allowed values: "inList", "excludedFromList" (compile issue) |
contains is valid, but it takes value, not values. The allowed values listed follow the key you sent; see Static filters. |
Custom categorical palette colors require a categorical or perEachMeasure color mode |
The chart is drawn in one colour. See Colouring types. |
Palette operations require a palette-based color mode |
The chart is drawn in one colour. See Colouring types. |
Format string can be changed only for custom format type |
Set the format type to custom first. See Date labels. |
Format type <type> is not allowed for this data item |
The format does not fit the date’s grouping. See Date labels. |
non_fresh_base_vspec (compile issue) |
Your baseVSpec already had data. Use createDefaultVSpec(type). |
Limitations
Section titled “Limitations”- Charts default to the built-in light theme; there is no theme channel.
- The package does not persist vSpecs.
- There are no React, Vue, or Svelte wrappers.
- Server-side rendering and Node imports are not supported, including the
/definitionssubpath. map,self-service,filter-bar,bookmark,button, and other Workbook-only objects are not included. The mountable registry above is the complete list.- Variable writes throw. Variables are read-only.
atomic-inputcompiles but cannot be mounted. Skip it.