--- url: https://chartbuddy.io/embed/docs/api/chart-data.md --- > Docs index: [llms.txt](https://chartbuddy.io/embed/docs/llms.txt) # 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: | Shape | When | Notes | |---|---|---| | **Input** (`ChartDataInput`) | What you author or patch | Partial OK. `donut` / `bubble` accepted. Merged over defaults. | | **Resolved** (`ChartData`) | What `getChartData()` returns | Resolved `chartType`. Full trees seeded. Safe to round-trip into `setChartData()`. | Machine-readable rules (same registry the runtime uses): [`chart-schema.json`](https://unpkg.com/@chartbuddy.io/embed/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). *** ## Recommended shell ```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 | Field | Role | See | |---|---|---| | `chartType` | Chart type id (`donut` / `bubble` also accepted) | [Chart types](https://chartbuddy.io/embed/docs/chart-types.md) · below | | `seriesData` | 2D grid (layout depends on type) | below | | `isDataTransposed` | Rows = series when `true` (default for most types) | below | | `orientation` | `'vertical'` | `'horizontal'` | [Orientation](https://chartbuddy.io/embed/docs/concepts/orientation.md) · [Axes](https://chartbuddy.io/embed/docs/axes.md) | | `title` / `subtitle` / `footnote` | Furniture text blocks | [Title, subtitle & footnote](https://chartbuddy.io/embed/docs/configuration/title-subtitle-footnote.md) | | `legend` | Visibility, placement, **`colors` palette** | [Legend](https://chartbuddy.io/embed/docs/configuration/legend.md) | | `backgroundColor` | Chart underlay (PNG export still needs its own `background` option) | [Visual QA](https://chartbuddy.io/embed/docs/quality-assurance/visual-qa.md) | | `axes` | Sides, roles, ticks, bounds, formats, breaks | [Axes](https://chartbuddy.io/embed/docs/axes.md) | | `annotations` | Arrows, level lines, totals, data-label formats | [Labels](https://chartbuddy.io/embed/docs/configuration/labels.md) · [Arrows](https://chartbuddy.io/embed/docs/configuration/arrows.md) · [Level lines](https://chartbuddy.io/embed/docs/configuration/level-lines.md) · [Number formats](https://chartbuddy.io/embed/docs/configuration/number-formats.md) | | `multilines` | Free text boxes | [Text boxes](https://chartbuddy.io/embed/docs/configuration/text-boxes.md) | | `canvas` | Internal width/height snapshot | [Canvas & sizing](https://chartbuddy.io/embed/docs/concepts/canvas.md) | | `bar` / `line` / `area` / `pie` / `scatter` / `waterfall` / `mekko` / `barMekko` / `combo` | Type-specific option bags | [Chart types](https://chartbuddy.io/embed/docs/chart-types.md) · below | | `seriesLabels` | Per-series / per-point label chrome | full export | | `chartPositionPercentages` | Plot margins inside the canvas | full export | | `id` / `version` | Document identity / version | runtime | 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 pass | Resolves to | Seeded defaults | |---|---|---| | `donut` | `pie` | `pie.innerRadiusRatio: 0.5` | | `pie` | `pie` | `pie.innerRadiusRatio: 0` | | `bubble` | `scatter` | larger 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](https://chartbuddy.io/embed/docs/chart-types/bar-mekko.md). ```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 | Value | Meaning | |---|---| | `true` (typical) | Rows after the header are series; columns are categories | | `false` | Opposite 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: | Types | Bag | |---|---| | `clusteredBar`, `stackedBar`, `stackedBar100` | `bar` | | `line` | `line` | | `stackedArea`, `stackedArea100` | `area` | | `pie`, `donut` | `pie` | | `scatter`, `bubble` | `scatter` | | `waterfall` | `waterfall` | | `mekko` | `mekko` | | `barMekko` | `barMekko` | | `combo` | `combo` | 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](https://chartbuddy.io/embed/docs/chart-types.md). *** ## 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 }, }, } ``` | Rule | Detail | |---|---| | Column keys | 0-based **data-column** indices: header cells **after** the row-label cell. `'End'` is header index 5 but data column **4**. | | `isTotal: true` | Show running total; ignore this column's own value | | `startBar: true` | Reset to zero and drop the connector (new sequence). A mid-chart opening bar needs **both** `isTotal` and `startBar`. | | `showSegments: true` | Draw a total as per-series segments instead of a solid bar | | Column 0 | Always forced to `isTotal` + `startBar`; unlike other totals it **does** use its own value (opening balance) | Full type page: [Waterfall](https://chartbuddy.io/embed/docs/chart-types/waterfall.md). *** ## 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 ``` | Behaviour | Detail | |---|---| | Omit `chartType` | Keeps the current type | | Deep-merge keys | `title`, `subtitle`, `footnote`, `legend`, `axes`, `canvas`, `annotations` | | Other top-level keys | Typically **replace** (including arrays such as `multilines`) | | Empty / useless patch | Rejected (`empty-patch`) when there is nothing to apply | See [Defaults & merging](https://chartbuddy.io/embed/docs/concepts/defaults.md). *** ## Validation `new Insight({ chartData })`, `setChartData`, `setData`, and `update` validate at the API boundary and **throw** `ChartDataValidationError` with structured issues. | Input | Behaviour | |---|---| | Unknown `chartType` | Throws (did-you-mean / `allowed`) | | Wrong type / enum / range | Throws (lists every path) | | Foreign option bag | Throws | | Ragged `seriesData` | Warns, still accepts | | Unknown keys | Always 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): | Code | Fix | |---|---| | `unknown-chart-type` / `not-in-enum` | Use `suggestion` or pick from `allowed` | | `wrong-type` | Coerce to `expected` | | `out-of-range` | Clamp to the bound in `expected` | | `series-data-shape` | Replace with a 2D array of string/number cells, not `{ name, data }` objects. `expected` names row/column minimums | | `ragged-series-data` | Warning – pad short rows | | `foreign-option-bag` | Move options into the bag named in `suggestion` | | `empty-patch` | Include `chartType` and/or `seriesData` | Full reference: [Validation](https://chartbuddy.io/embed/docs/quality-assurance/validation.md). *** ## 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** *** ## Related * [Data model](https://chartbuddy.io/embed/docs/concepts/data-model.md) – layouts overview * [Defaults & merging](https://chartbuddy.io/embed/docs/concepts/defaults.md) * [Insight API](https://chartbuddy.io/embed/docs/api.md) – mount / update / events * [Visual QA](https://chartbuddy.io/embed/docs/quality-assurance/visual-qa.md) – observe after mount * Docs index: [llms.txt](https://chartbuddy.io/embed/docs/llms.txt) · schema: [chart-schema.json](https://unpkg.com/@chartbuddy.io/embed/chart-schema.json)