Skip to content

Visualizations reference

13 min read · Updated 2 Nov 2026

@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.

Do not build or edit a vSpec directly. Use one of the package’s two supported APIs:

  1. Definitions create a chart: a declarative description compiles into a fresh vSpec.
  2. 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.md
  • node_modules/@astrato-io/visualizations/docs/operations/index.md

Check package versions before development, including when reusing an existing installation.

Externally hosted app:

Terminal window
npm install @astrato-io/visualizations @astrato-io/sdk

Data App:

Terminal window
npm install @astrato-io/visualizations @astrato-io/app-runtime

Subpath 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.
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 vSpec
meta.requirements; // data requirements
meta.examples;
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.

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 specification
compilation.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 use atomic-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 including non_fresh_base_vspec (your base vSpec already had data — use createDefaultVSpec), unknown_field_key, requirements_not_met, and operation_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.

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 format off to keep the format the semantic layer sets, including its currency. Any formatType replaces it.
  • Dates. Leave format off a date dimension or column to show a date, or set a date format, such as { formatType: 'custom', formatString: 'MMM yyyy' }. none draws the raw epoch number, and so does inherit unless 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.

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'] },
],
};
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' }],
});
  • applyRuntimeOperations targets 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: false while later requests still run.
  • Each operation has a 1-second execution timeout.

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:

  1. Read its schema in the operations reference for your visualization type, or obtain payloadSchema from getVisualizationMetadata(type).operations.items in the browser.
  2. Validate your payload against that schema yourself.

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;

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.

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.

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.

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.

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,
});

To mount directly:

const container = document.getElementById('chart-slot'); // must have non-zero size
if (!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 state
rendered.status; // 'rendered' | 'error' | 'unmounted' | 'timeout'
// handle.unmount() when done

handle.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 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]);
  • 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.items before applying them.
  • Categorical charts can use setChartPalette with a built-in palette name, or setCustomPaletteColors with an ordered list of colours. setColoringPersistentColors accepts a boolean: false assigns colours in domain order, while true hashes 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.

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.

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.

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).
  • 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 /definitions subpath.
  • 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-input compiles but cannot be mounted. Skip it.