Skip to content

Reusable demo starter

3 min read · Updated 30 Oct 2026

Use this starter during demo preparation, before starting the timer. It provides a two-column chart shell that stacks on narrow screens. Supply the verified data definitions and page text for the requested build; the starter contains no bundled dataset. The standard question coverage and capability requirements apply when using this shell. Its example definitions do not replace exploration, missing question authoring or verification of the selected semantic and chart features.

Follow the matching Data App or externally hosted quickstart first. Reuse its installed packages, authentication and local session.

Download these files into the frontend’s source directory, removing the final .txt from the TypeScript filenames:

The shell imports @astrato-io/visualizations. The Data App adapter also imports @astrato-io/app-runtime. Use these public packages through your chosen quickstart. Do not use the Data App adapter in an externally hosted app; pass that app’s established client and data binding to createDemoDashboard instead.

The Data App adapter accepts a buildViews function receiving the live field catalog. Resolve fields by the exact names and entity kinds reported in that catalog. A stored dimension and a raw column can share a name; select the entity required by the question before checking ambiguity. Fail on missing or multiple matches within that kind. The compiler checks every definition against that metadata before mounting any chart.

For example, this deliberately incomplete plan demonstrates one category chart. Replace the bracketed names and page text with verified context, choose the correct aggregation, and add the other views from the demo brief before timing the build:

import { startDemo } from './start-demo.js';
const root = document.querySelector<HTMLElement>('#app');
if (!root) throw new Error('App root is missing.');
// startDemo resolves to the dashboard; keep the promise for teardown.
const demo = startDemo(root, {
title: '[Verified page title]',
scope: '[Verified period, units and filter scope]',
buildViews(fields) {
const find = (name: string, kind: 'dimension' | 'measure' | 'calculation' | 'field') => {
const matches = fields.filter((field) => field.name === name && field.kind === kind);
const field = matches[0];
if (matches.length !== 1 || !field) {
throw new Error(
`Resolve exactly one field named ${name}; available names: ${fields.map((f) => f.name).join(', ')}`,
);
}
return field.fieldKey;
};
return [
{
id: 'category-chart',
title: '[Verified category comparison]',
subtitle: '[Units and period]',
definition: {
version: 1,
type: 'bar',
data: {
dimensions: [{ ref: 'category', fieldKey: find('[Stored dimension name]', 'dimension') }],
measures: [{ ref: 'value', fieldKey: find('[Raw numeric field name]', 'field'), aggregation: 'SUM' }],
},
},
},
];
},
});
demo.catch((error) => console.error(error)); // startDemo also shows the message in the root
// On permanent application teardown, before discarding the app:
async function teardown(): Promise<void> {
const dashboard = await demo.catch(() => undefined); // a failed start has already released the app
await dashboard?.destroy();
}

A raw numeric field’s aggregation is a business choice. Named measures and calculations retain their semantic meaning; do not add an aggregation merely to copy this example. Consult the chosen type’s definition.

Definitions do not automatically preserve a saved Business Question’s filters, parameters, ranking or local calculations. Carry these into the supported definition or custom-query path and verify the result; see From question to chart.

  • One shared selection group, with Apply, Discard and Clear all controls and selection counts.
  • One live metadata catalog reused by definition compilation and data bindings.
  • A resolved chart host size, loading feedback, rendering failure and timeout feedback with Retry.
  • Hide/show controls that unmount hidden charts. Serialized lifecycle operations prevent a late mount from leaving a hidden chart querying. Readiness runs outside that lifecycle chain.
  • Permanent teardown that releases subscriptions, unmounts charts and removes instances before the Data App adapter destroys its runtime.

The shell exposes setEmpty(viewId, boolean) for callers with current, verified row-count evidence. It does not infer emptiness from readiness: a rendered chart may have no rows. Wire that state to current query results for views you draw yourself, and update it after applied filters change. Mounted visualizations retain their own no-data rendering. Keep object states and query-result handling appropriate to the selected types.

This starter leaves chart-specific operations, period controls, exports and alternate layouts to the requested app. Use the component chooser rather than adding every control.

Check the copied starter against your installed packages. Render all prepared views in the established session, compare a displayed value to query evidence, apply and discard a selection, hide and show a chart, and check a narrow viewport. Run the supported local build command.

The example’s API and types are checked locally. A live tenant session is still required to establish working data bindings, selections and results; the static example does not guarantee completion in under a minute. Retain its verified project context for the timed demo and recheck affected paths when dependencies or the model change.