Skip to content

Data and Bindings

Pivot Charts project the committed Pivot result into a renderer-neutral PivotChartDataset. The standard matrix is efficient for category/series rendering, while the normalized analytical frame supplies stable fields and observations for relationship, hierarchy, range, flow, financial, KPI, and other specialized chart types.

Interactive example

Datasets and bindings

Compare the chart with its row-major data matrix, including null and formatted values.

The example loads when visible.

interface PivotChartDataset {
revision: number;
source: 'client' | 'server';
categories: PivotChartCategory[];
series: PivotChartSeries[];
values: Array<Array<number | null>>;
displayValues?: Array<Array<string | null>>;
hierarchies?: {
row?: PivotChartHierarchyDefinition;
column?: PivotChartHierarchyDefinition;
};
analyticalFrame?: PivotChartAnalyticalFrame;
}

values is row-major: values[categoryIndex][seriesIndex]. Optional displayValues preserve the Pivot’s formatted values for chart labels, tooltips, data tables, and accessibility text. Categories and series retain stable IDs, member paths, labels, member kinds, measures, and drill hierarchy metadata.

The dataset revision identifies the committed Pivot snapshot that produced the frame. Linked refreshes and drill requests use it to reject stale work.

interface PivotChartAnalyticalFrame {
fields: PivotChartFieldDescriptor[];
observations: PivotChartObservation[];
matrixCells?: Array<{
categoryId: string;
seriesId: string;
observationId: string;
measureFieldId: string;
}>;
hierarchies?: PivotChartDataset['hierarchies'];
derived?: {
bins?: PivotChartJsonValue[];
quantiles?: PivotChartJsonValue[];
distributions?: PivotChartDistribution[];
ranges?: PivotChartJsonValue[];
flowEdges?: PivotChartJsonValue[];
financial?: PivotChartJsonValue[];
geographicFeatures?: PivotChartJsonValue[];
};
}

Fields declare a stable ID, label, dimension/measure role, semantic type, source field, aggregator, and optional format. Each observation stores dimension values separately from numeric measures and optional display measures. Providers and custom renderers can use this metadata independently of visible labels. Built-in statistical charts use the typed distribution views; built-in measure-channel bindings resolve matrix series identities.

Use matrixCells when supplied observations or measure fields have their own IDs. Each entry maps one matrix cell to one measure on a tidy observation. A single observation can therefore carry several measures:

dataset.analyticalFrame = {
fields: [
{ id: 'region-code', label: 'Region', role: 'dimension', semanticType: 'identifier' },
{ id: 'revenue', label: 'Revenue', role: 'measure', semanticType: 'number' },
{ id: 'margin', label: 'Margin', role: 'measure', semanticType: 'number' },
],
observations: [{
id: 'report-row-42',
dimensions: { 'region-code': { value: 'north', label: 'North' } },
measures: { revenue: 120, margin: 18 },
}],
matrixCells: [
{ categoryId: 'north', seriesId: 'sales-series', observationId: 'report-row-42', measureFieldId: 'revenue' },
{ categoryId: 'north', seriesId: 'margin-series', observationId: 'report-row-42', measureFieldId: 'margin' },
],
};
// The corresponding matrix must contain [[120, 18]], in those series positions.

Map every matrix cell once, including null cells, and reference every supplied observation. IDs must exist, referenced fields must be measures, and each observation/measure pair maps to one cell. Matrix values remain authoritative; conflicting numbers or display strings are rejected. Extra dimension metadata and ancillary fields remain metadata; they do not redefine matrix aggregation.

Projection retains mapped observation IDs and metadata for surviving cells. Removing a selected series removes its mapped measure, rather than retaining that excluded value in a stale analytical frame. Unlinking and JSON restoration retain the explicit mapping. Existing generated canonical frames remain supported; arbitrary supplied IDs without a mapping are rejected with an actionable error. Observation IDs are opaque identities, so applications should not construct or split generated IDs.

PivotChartBindings declares renderer channels. For the built-in renderer, measure channels refer to IDs from dataset.series, and the flow dimensions use pivot:category and pivot:series. Empty and omitted channels are equivalent.

Channel groupChannelsUsed by
Value selectionvalueStatistical distributions, histogram, strip/dot, waffle, calendar, and annotated time series.
Relationshipx, y, sizeScatter, bubble, density, and connected-scatter views.
Flowsource, target, weightSankey and chord.
Rangestart, end, low, highTimeline, range, confidence, and error views.
Financialopen, high, low, close, volumeCandlestick, OHLC, and volume-price.
KPIactual, target, varianceBullet, gauge, KPI, and progress gauge.

Other typed channels are extension points for custom renderers. The built-in renderer derives ordinary categories, hierarchy, polar layout, and facets from the matrix; facetRow, facetColumn, label, tooltip, shape, angle, and radius do not configure these native views. Calendar accepts color as a fallback value-series binding. Use colors and the label/tooltip formatter APIs for native appearance. Preserving an extra analytical field does not make it a native rendering channel.

The chart gallery checks required channel counts before selection. For relationship charts, the Data panel infers distinct X/Y measures when possible and prevents the same field from satisfying both required channels.

await chart.update({
chartType: 'bubble',
bindings: {
x: ['measure:cost:sum'],
y: ['measure:revenue:sum'],
size: ['measure:units:sum'],
},
});

Use matrix series IDs for these native measure bindings, not visible labels or arbitrary analytical-frame field IDs. Some specialized channels can infer an unambiguous semantic alias such as low or close; explicit IDs are preferable when captions are localized or several candidates match.

seriesIds selects a subset of dataset series. Types whose catalog definition uses firstAvailable select one series even when the Pivot exposes several. Types marked all retain the compatible series selection.

When the Pivot places values on rows, the category carries its measure. categoryMeasure selects the required { prop, aggregator } pair without rewriting row paths or pretending the measure is a column series.

const dataset = await charts.getChartData({
chartType: 'line',
seriesIds: ['series:2026'],
categoryMeasure: {
prop: 'revenue',
aggregator: 'sum',
},
});
Interactive example

Chart queries and totals

Compare chart projections that include grand totals, subtotals, both, or neither.

The example loads when visible.

totals accepts four modes:

ModeIncluded members
noneLeaf and group members only.
grandInclude grand totals.
subtotalsInclude subtotals.
allInclude grand totals and subtotals.

The default is none. Totals keep their semantic member kind, so charts, tooltips, data tables, drill logic, and persisted models can distinguish them from ordinary categories. Totals select the generated members available in the current Pivot scope; they do not enable totals disabled in the Pivot configuration. At a semantic root, parent aggregates already represent the immediate members, so selecting subtotals need not add separate row categories.

A linked chart can author its own query without changing the shared Pivot layout or adding a shared filter contribution. A query can select registered row and column fields, select existing configured measures, add member filters, sort categories, and rank immediate row members with Top/Bottom N.

await chart.update({
query: {
slice: { rows: ['region'], columns: ['year'] },
filters: { status: ['Open'] },
topBottom: {
direction: 'top',
count: 5,
measure: { prop: 'sales', aggregator: 'avg', valueIndex: 0 },
otherLabel: 'Other regions',
},
},
});
// Reset a linked chart to the current Pivot query.
await chart.update({ query: {} });

The fields and measure identities must exist in the committed Pivot configuration. Use id or the original configured valueIndex when the same source measure appears more than once. Queries cannot define new aggregators or calculated measures.

query updates replace the whole object. Omit it to preserve its authored settings; pass {} to reset a linked chart. Chart-local member filters intersect inherited member selections and chart-owned shared filters, including when both constrain the same field. In chart-query member selections, null denotes both missing and null source values, and signed zeros use the same member identity.

Top/Bottom N ranks the immediate children of the current row scope. Ranking uses the selected raw-measure aggregator within the requested column scope. Ties retain source order and missing scores rank last. Without an explicit sort, retained members follow ranking order and Other follows them.

Other contains the excluded source transactions, which are aggregated again by Pivot. This preserves averages, distinct counts, custom aggregators, and statistical samples. Ranking changes member presentation order while preserving source transaction order for custom aggregators and samples. It never adds displayed averages or distinct counts. Without Other, excluded members also leave the query’s totals; with Other, their transactions remain. Other is a synthetic member and cannot drill into fabricated children, filter the shared source, or identify a peer-chart member.

charts.getDataShapingCapabilities() reports current support and reasons. Unsupported inherited ranking, aggregate filters, or opaque filter execution fail with a reason instead of discarding those constraints. Ranking or sorting by a calculated measure is unsupported. Server queries require an opted-in complete-data provider. The native query path conservatively rejects Advanced Filter and custom Core filter evaluators, including when they appear inactive, because their public state does not establish an exact source predicate for an isolated query.

Changing the slice layout resets chart drill and viewport state. A chart with an independent layout uses chart-local or application-controlled drill; Pivot-synchronized drill requires the shared layout. A sort preserves semantic viewport endpoints. Filtering and ranking retain valid scopes or fall back to a valid ancestor.

Unlinking freezes the shaped dataset and its descriptive query. Recalculating or resetting that query requires relinking to the source.

In the Data tab, valid field, measure, sort, rank, and member-filter edits update automatically. Incomplete or invalid edits remain local with validation feedback. Drafts survive tab changes and earlier asynchronous acknowledgments. Reset query clears local and applied query settings. Series/binding and Pivot connection controls also apply immediately.

Fields already used on either axis cannot be added again. Independent layouts show a drill-synchronization hint and require Chart only or Application event for independent query edits. Calculated measures remain available for projection, while sort and ranking selectors disable them.

Member lookup searches and pages in batches of 50, retains selected raw values across pages, cancels obsolete requests, and provides loading, empty, error, and retry states. Labels and summaries use localeText.query.formatMemberValue; formatting never changes the predicate’s raw value. Query status and recovery text are customizable through localeText.query.description, immediateChanges, independentLayoutSync, memberLoading, memberEmpty, and retryMembers. UI policies can hide or disable query.independentLayoutSync, query.memberStatus, query.retryMembers, and controls.connectionHint, alongside the existing query and connection controls.

A linked chart follows the current committed Pivot. Data, filters, sorting, group expansion, source, Pivot model, and relevant theme changes schedule coalesced refreshes.

Turning off Keep linked to Pivot stores the current bounded normalized dataset in model.dataset. Later Pivot changes do not affect that chart. Relinking removes the stored snapshot and projects the latest committed Pivot state. Restore validates both linked models and embedded unlinked datasets.

The dialog’s context strip identifies the linked chart or saved snapshot, current category and series counts, totals policy, hierarchy, and available Pivot/chart member filters. It also states when additional provider or Pivot filters may apply; missing metadata is not treated as proof of an unfiltered source. Unlinked charts retain their captured source context.

The Data details button opens a separate popup with the current data size, link status, row and column hierarchy, totals policy, and member filters. Opening it leaves the plot size unchanged. Close it with Escape, the close button, or a click outside the popup.

Interactive example

Chart data and axes

Inspect the chart's current data in the native table and switch back to the chart.

The example loads when visible.

The header’s View data action replaces the plot with a read-only RevoGrid for the active drill frame. It:

  • uses the same category order, series, and formatted display values as the chart;
  • represents missing observations consistently;
  • virtualizes rows and columns, supports selection and column resizing, and provides keyboard-operable sorting with sort announcements;
  • does not alter the chart model, link state, Pivot, or drill state;
  • switches back through View chart.

getChartData can request the immediate children of a semantic parent:

const dataset = await charts.getChartData({
chartType: 'groupedColumn',
totals: 'none',
scope: {
rowPath: ['Europe'],
columnPath: [2026],
},
});

Chart creation and getChartData() use default acquisition limits of 10,000 categories, 250 series, and 50,000 data points. The raw projector uses a 1,000-category default when no chart type is supplied. Plugin and chart-authored limits override these defaults; see server data and performance for precedence and rendering policies. The plugin validates limits, actual counts, matrix dimensions, observations, bindings, and chart compatibility. Unsafe projections fail with a PivotChartError rather than silently changing persisted data.

Previous: Chart types and compatibility · Next: Drill-down and interactions · API reference

Box plots and violin charts use original numeric observations for one category and measure. They never treat unrelated aggregated measure columns as samples. Client Pivot queries the committed, filtered original source lazily when a statistical chart is selected; calculated measures cannot supply raw samples. The sample allocation is bounded by maxDataPoints and cached per data revision and chart scope. No observations are silently sampled or truncated.

Server providers and standalone datasets can supply distributions. Preserve any existing matrixCells, fields, and observations when adding derived statistics:

dataset.analyticalFrame = {
...dataset.analyticalFrame,
fields: dataset.analyticalFrame?.fields ?? [],
observations: dataset.analyticalFrame?.observations ?? [],
derived: {
...dataset.analyticalFrame?.derived,
distributions: [{
kind: 'samples',
categoryId: dataset.categories[0].id,
seriesId: dataset.series[0].id,
samples: [1, 2, 3, 4, 100],
}],
},
};

Each category/series pair must be unique and refer to existing matrix members. Samples must be finite numbers. Empty samples omit that mark; singleton and constant samples remain valid. Quartiles use linear interpolation and whiskers use the outermost observations within 1.5 interquartile ranges. Outliers retain the actual observation value for tooltips and click events.

A provider can instead supply a kind: 'summary' entry with count, minimum, lowerQuartile, median, upperQuartile, maximum, lowerWhisker, and upperWhisker, plus optional outliers and mean. Violin charts additionally require density: [{ value, density }], ordered by strictly increasing value, with finite nonnegative density and at least one positive density. Precomputed summaries draw the supplied statistics; the renderer does not invent samples.

Derived distributions survive selection, caching, unlinking, and JSON model save/restore. The older opaque derived.quantiles field remains accepted as metadata but does not enable statistical charts. Range interval charts retain their separate min/max semantics across bound matrix series.

Canonical field descriptors retain their semantic type and format through selection and model persistence. Generated observation IDs are opaque; do not construct or split them. For provider-owned observation IDs and fields, supply the complete matrixCells mapping. The matrix remains authoritative for measures and display strings; mapped metadata survives selection and model persistence.

annotatedTimeSeries, smallMultiples, columnLine, and areaLine accept the typed policy axes: { x: { scale: 'category' | 'time' } }. Category spacing is the default. Select Customize → X-axis scale → Time to position points by actual elapsed time. Time input comes from the last semantic category path member, never its localized display label. Accepted values are valid ISO calendar dates (UTC), ISO timestamps with an explicit zone, and numeric epoch milliseconds. Ambiguous civil timestamps, impossible dates, and empty total-member paths fail with INVALID_DATA_SHAPE; exclude totals when using a time axis.

Time coordinates sort chronologically with stable original ordering for equal instants. Tooltips and drill events retain the original category and value. Every small-multiple panel shares the same exact X domain. Singleton/equal instants are centered. Tick formatting uses UTC and available panel width. Missing, null, or nonfinite Y values split paths before point sampling, so sampled lines cannot connect across missing observations.

Axis settings survive JSON model persistence and linked refreshes. An axes update replaces the axes configuration; axes: {} restores defaults. A programmatic time policy on an unsupported chart type is rejected. The gallery clears incompatible policies when switching chart types.

Combination value axes and series overrides

Section titled “Combination value axes and series overrides”

columnLine and areaLine support independent primary (axes.y) and secondary (axes.ySecondary) value axes. Each accepts scale: 'linear' | 'log', finite min/max bounds, title, a tick-only unit, and a serializable tickFormat:

await chart.update({
axes: {
y: { min: 0, max: 100_000, title: 'Revenue', unit: 'USD',
tickFormat: { notation: 'compact', maximumFractionDigits: 1 } },
ySecondary: { scale: 'log', min: 1, max: 1_000, title: 'Orders' },
},
seriesOverrides: {
[revenueSeriesId]: { type: 'column', yAxis: 'primary' },
[ordersSeriesId]: { type: 'line', yAxis: 'secondary' },
},
});

Overrides use stable dataset series IDs and accept type: 'column' | 'line' | 'area' and yAxis: 'primary' | 'secondary'. Unspecified series retain the legacy combination defaults: the first series is column/area, the remaining series are lines, and every series uses the primary axis. Dormant IDs remain in the model and apply when those series return. Multiple columns are grouped side by side; areas draw before columns and lines. Time columns reserve space at the endpoint groups and use bounded widths based on distinct instant gaps.

A log axis requires positive observations and bounds, and supports line series only. Invalid assignments or domains fail with INVALID_DATA_SHAPE before rendering. Explicit domains clip geometry without changing canonical values, tooltips, or exports; fully clipped observations have no focus targets. Linear column/area baselines use zero clamped to the visible domain. A secondary axis reserves space only when an assigned active series has finite observations.

The primary title falls back to presentation.yAxisTitle; the secondary title is independent. Units are never inferred. tickFormat.notation is standard, compact, or scientific; optional minimum/maximum fraction digits are integers from 0 to 20 with minimum no larger than maximum. Authored formatting uses the runtime Intl locale; without it, ticks retain the locale text number formatter. Runtime label customization receives axis identity y or ySecondary.

Axes and overrides are independently replaced on update. Omit a property or use undefined to retain it; use {} to reset it. JSON restore, linked refresh, and drill preserve both. Other chart types reject unsupported authored value axes/assignments; clear these properties when switching programmatically.