Skip to content

chartData schema ​

chartData (also called cd) is the serializable chart document. You pass it into new Insight(), setChartData(), and update(). You read the resolved form with getChartData().

Two shapes matter:

ShapeWhenNotes
Input (ChartDataInput)What you author or patchPartial OK. donut / bubble accepted. Merged over defaults.
Resolved (ChartData)What getChartData() returnsResolved chartType. Full trees seeded. Safe to round-trip into setChartData().

Machine-readable rules (same registry the runtime uses): chart-schema.json.


Minimal ​

js
{
  chartType: 'clusteredBar',
  isDataTransposed: true,
  seriesData: [
    ['', 'Q1', 'Q2', 'Q3'],
    ['Revenue', 100, 112, 125],
  ],
}

chartType + seriesData are enough to mount. Everything else deep-merges from defaults (or stays unset until you need it).


js
{
  chartType: 'clusteredBar',
  isDataTransposed: true,
  seriesData: [/* … */],
  orientation: 'vertical', // or 'horizontal'
  title: { visible: true, text: 'Revenue' },
  subtitle: { visible: false, text: '' }, // hide default placeholder
  legend: { visible: true, colors: ['#2563eb', '#64748b'] },
  backgroundColor: '#ffffff',
}

Add axes, annotations, type bags (bar, line, …), and furniture only when you need them – or export a full tree from the editor and trim.


Top-level map ​

FieldRoleSee
chartTypeChart type id (donut / bubble also accepted)Chart types · below
seriesData2D grid (layout depends on type)below
isDataTransposedRows = series when true (default for most types)below
orientation'vertical' | 'horizontal'Orientation · Axes
title / subtitle / footnoteFurniture text blocksTitle, subtitle & footnote
legendVisibility, placement, colors paletteLegend
backgroundColorChart underlay (PNG export still needs its own background option)Visual QA
axesSides, roles, ticks, bounds, formats, breaksAxes
annotationsArrows, level lines, totals, data-label formatsLabels · Arrows · Level lines · Number formats
multilinesFree text boxesText boxes
canvasInternal width/height snapshotCanvas & sizing
bar / line / area / pie / scatter / waterfall / mekko / barMekko / comboType-specific option bagsChart types · below
seriesLabelsPer-series / per-point label chromefull export
chartPositionPercentagesPlot margins inside the canvasfull export
id / versionDocument identity / versionruntime

Unknown top-level keys are allowed (forward-compatible). Wrong types, foreign option bags, and unknown chartType values are rejected.


chartType ​

Resolved values (cd.chartType from getChartData()):

clusteredBar · stackedBar · stackedBar100 · line · stackedArea · stackedArea100 · pie · scatter · waterfall · mekko · barMekko · combo

Donut & bubble ​

donut and bubble are shortcuts that seed defaults so the chart looks like what you asked for:

You passResolves toSeeded defaults
donutpiepie.innerRadiusRatio: 0.5
piepiepie.innerRadiusRatio: 0
bubblescatterlarger point diameter
js
{ chartType: 'donut', seriesData: [['Category', 'Value'], ['North', 45]] }

Do not also set innerRadiusRatio unless you want a different hole. getChartData() returns pie with innerRadiusRatio: 0.5. That round-trips correctly.


seriesData layouts ​

Layout is fixed by chartType (see the registry / chart-schema.json). Cells are string | number | boolean | null. Not { name, data } series objects.

Bar / line / area / stacked / waterfall / mekko / combo (seriesRows) ​

With isDataTransposed: true (usual): row 0 is the category header; later rows are series.

js
[
  ['', 'Q1', 'Q2', 'Q3'],
  ['Revenue', 100, 112, 125],
  ['Costs', 60, 66, 70],
]

Minimum: 2 rows × 2 columns (header included).

Pie / donut (categoryValue) ​

js
[
  ['Category', 'Value'],
  ['North', 45],
  ['South', 30],
]

Scatter / bubble (pointRows) ​

No transpose. Row 0 names metrics; later rows are points (x, y, optional size, optional group).

js
[
  ['', 'Metric X', 'Metric Y', 'Size', 'Group'],
  ['Point 1', 10, 15, 8, 'A'],
]

Minimum: 2 rows × 3 columns.

Bar Mekko (widthRowThenHeightRows) ​

Row 1 after the header sets widths (not drawn as a series). Later rows stack as heights on a real value axis. See Bar Mekko.

js
[
  ['', 'Enterprise', 'Mid-market', 'SMB'],
  ['Accounts', 120, 85, 200],       // width
  ['Core revenue', 48, 16, 8],      // height
  ['Add-ons', 14, 12, 10],
]

Minimum: 3 rows × 2 columns.

Ragged grids ​

Unequal row lengths warn and still load. Pad short rows when you care about clean columns.


isDataTransposed ​

ValueMeaning
true (typical)Rows after the header are series; columns are categories
falseOpposite spreadsheet orientation

Scatter / bubble ignore transpose (pointRows). When unsure, keep true for bar/line/area/waterfall/combo and match the samples.


Type-specific option bags ​

Each chart type reads one bag:

TypesBag
clusteredBar, stackedBar, stackedBar100bar
lineline
stackedArea, stackedArea100area
pie, donutpie
scatter, bubblescatter
waterfallwaterfall
mekkomekko
barMekkobarMekko
combocombo

Putting options in the wrong bag (e.g. chartType: 'pie' with bar: {…}) throws (foreign-option-bag). Resolved snapshots from getChartData() may carry every bag (defaults seed them); that is normal when round-tripping.

Examples:

js
{ chartType: 'donut', pie: { innerRadiusRatio: 0.7 } }

{ chartType: 'combo', combo: { seriesTypes: { 0: 'bar', 1: 'line' } } }

{ chartType: 'barMekko', barMekko: { sort: 'heightDesc' } }

Per-type fields and recipes: Chart types.


Waterfall: do not invent totals ​

Total columns are computed. Mark the column and leave its cell empty. A value typed into an isTotal column is ignored. A closing figure typed without isTotal becomes another contribution bar (the bridge then ends near ~2×).

js
{
  chartType: 'waterfall',
  isDataTransposed: true,
  seriesData: [
    ['', 'Start', 'Price', 'Volume', 'Mix', 'End'],
    //  ^0       ^1       ^2        ^3     ^4   <- waterfall.columns keys
    ['Bridge', 100, 18, -8, 5, null], // End carries NO value
  ],
  waterfall: {
    columns: {
      4: { isTotal: true }, // closing bar = 115
    },
  },
}
RuleDetail
Column keys0-based data-column indices: header cells after the row-label cell. 'End' is header index 5 but data column 4.
isTotal: trueShow running total; ignore this column's own value
startBar: trueReset to zero and drop the connector (new sequence). A mid-chart opening bar needs both isTotal and startBar.
showSegments: trueDraw a total as per-series segments instead of a solid bar
Column 0Always forced to isTotal + startBar; unlike other totals it does use its own value (opening balance)

Full type page: Waterfall.


Partial patches and merging ​

js
insight.setData(seriesData);                 // data only
insight.update({ title: { text: 'FY26' } }); // any partial
insight.setChartData({ seriesData: […] });   // chartType optional
insight.update();                            // redraw only
BehaviourDetail
Omit chartTypeKeeps the current type
Deep-merge keystitle, subtitle, footnote, legend, axes, canvas, annotations
Other top-level keysTypically replace (including arrays such as multilines)
Empty / useless patchRejected (empty-patch) when there is nothing to apply

See Defaults & merging.


Validation ​

new Insight({ chartData }), setChartData, setData, and update validate at the API boundary and throw ChartDataValidationError with structured issues.

InputBehaviour
Unknown chartTypeThrows (did-you-mean / allowed)
Wrong type / enum / rangeThrows (lists every path)
Foreign option bagThrows
Ragged seriesDataWarns, still accepts
Unknown keysAlways allowed

Prefer checking before mount:

js
import { validateChartData } from '@chartbuddy.io/embed';

const { valid, errors, warnings } = validateChartData(candidate);
// issue: { path, code, message, severity, expected?, received?, allowed?, suggestion? }

Branch on code, not message (messages may change):

CodeFix
unknown-chart-type / not-in-enumUse suggestion or pick from allowed
wrong-typeCoerce to expected
out-of-rangeClamp to the bound in expected
series-data-shapeReplace with a 2D array of string/number cells, not { name, data } objects. expected names row/column minimums
ragged-series-dataWarning – pad short rows
foreign-option-bagMove options into the bag named in suggestion
empty-patchInclude chartType and/or seriesData

Full reference: Validation.


Full document workflow ​

Hand-authoring every axis tick and annotation is painful. Prefer:

  1. Mount with a minimal or recommended shell
  2. Polish in edit mode (editable: true)
  3. Snapshot:
js
await insight.ready;
const full = insight.getChartData();
// or insight.exportConfig() for a JSON download

Expect trees such as canvas, axes, annotations, multilines, type bags, seriesLabels, and chartPositionPercentages. Round-trip that object with setChartData(full) when you need a complete starting point, then patch.


Pitfalls ​

  • Generating a chart type that is not in the registry (bubbleChart3D) – validate first
  • Putting bar options on a pie (foreign bag)
  • Wrong seriesData layout for the type (pie as series-rows, scatter transposed)
  • Waterfall closing value without isTotal (double-counts the bridge)
  • Wrong waterfall column index (header position vs data-column index)
  • Expecting backgroundColor alone to appear in PNG export – pass toPngBase64({ background })
  • Expecting getChartData() to keep chartType: 'donut' / 'bubble' – it returns pie / scatter with the seeded defaults
  • Deep-merging arrays by hand – multilines and similar usually replace

Developer documentation for Chartbuddy Embed · Not the end-user Help Center · Help Center