Pivot Charts
Pivot Charts turn the current Pivot result into an interactive analytical view. Pivot includes the renderer-neutral chart lifecycle, a first-party SVG renderer, a searchable chart gallery, semantic drill-down, a current-frame data table, persistence, export, localization, and server-side contracts. No separate charting dependency is required.
PivotChartsPlugin owns chart data, lifecycle, persistence, drill,
synchronization, and rendering. The optional PivotChartsUiPlugin adds the
built-in controls for embedded workspaces, the modeless dialog, and the
Pivot Chart… context-menu action. Applications that only need a chart or
their own controls can install only the headless plugin.
Choose a guide
Section titled “Choose a guide”Create a Pivot chart
Comparing multiple series across categories.
| Guide | What it covers |
|---|---|
| Chart types and compatibility | All 63 built-in types, required data channels, recommendations, and compatibility rules. |
| Data and bindings | Analytical frames, series and channel selection, totals, linking, data view, and limits. |
| Drill-down and interactions | Cell-aware creation, breadcrumbs, synchronization modes, tooltips, legends, keyboard behavior, and events. |
| Customization and export | Presentation, chart options, renderer styling, localization, PNG/PDF, and image encoders. |
| Headless API and persistence | Plugin methods, PivotChartRef, model restore, custom renderers, and lifecycle events. |
| Server data and performance | Complete-data providers, semantic scopes, cancellation, validation, dense data, and geographic capabilities. |
Register Pivot Charts
Section titled “Register Pivot Charts”For a chart-only integration, register the Pivot and headless chart plugins:
import { PivotChartsPlugin, PivotPlugin, createPivotChartsRenderer,} from '@revolist/pivot';
grid.pivotCharts = { renderer: createPivotChartsRenderer(),};grid.plugins = [PivotPlugin, PivotChartsPlugin];grid.pivotCharts is observable. Replacing the configuration updates locale
text and defaults, invalidates cached chart data, and refreshes linked charts.
This is where renderer options, a remote data provider, defaults, limits,
localization, and drill synchronization belong. The application container is
supplied later, when it exists.
Use charts without a grid
Section titled “Use charts without a grid”@revolist/pivot/charts provides a standalone controller for an ordinary DOM
container. It creates no <revo-grid>, Pivot plugin, source query or editor.
import { createStandalonePivotChart, createStandalonePivotChartDataset } from '@revolist/pivot/charts';import '@revolist/pivot/styles.css';
const container = document.createElement('div');container.style.cssText = 'width: 640px; height: 420px';document.body.append(container);const dataset = createStandalonePivotChartDataset( [{ month: 'January', revenue: 120 }, { month: 'February', revenue: null }], { category: { field: 'month' }, series: [{ field: 'revenue', label: 'Revenue' }] },);const chart = await createStandalonePivotChart({ container, dataset, chartType: 'line' });await chart.update({ presentation: { title: 'Monthly revenue' } });const csv = chart.getCSV();const saved = JSON.stringify(chart.model);chart.destroy(); // Call when the application removes the host.const restored = await createStandalonePivotChart({ container, model: JSON.parse(saved) });restored.destroy();The builder maps each row to one category and each explicit numeric field to one
series. Values must be finite numbers or explicit null. Typed category keys
must be unique; duplicate keys are rejected, never aggregated. Its identity
measure metadata is descriptive. Use the headless Pivot controller when source
aggregation is needed.
category.label names a source field containing each row’s string display
label; series.label supplies a literal series caption. For example, this
keeps 'jan' as the category identity while displaying 'January':
const labeled = createStandalonePivotChartDataset( [{ month: 'jan', monthName: 'January', revenue: 120 }], { category: { field: 'month', label: 'monthName' }, series: [{ field: 'revenue', label: 'Revenue' }] },);Omit category.label to display the category key. When provided, the named
field must contain a string in every row. Rename the data-table category header
with localeText.dataTable.categoryHeader.
The controller owns data/settings snapshots. Updates run in order and capture
caller patches immediately. undefined preserves an owner; {} resets it;
arrays replace. Presentation is a replacement owner, so include titles you want
to retain. Dataset replacement requires a complete canonical dataset. Save an
unlinked chart.model and supply model instead of dataset/settings to restore.
A creation signal cancels pending creation and disposes the chart on later
abort. destroy() is idempotent; other operations reject after disposal.
interaction.mode: 'select' commits local selection; 'event' calls
onSelectionRequest without committing. Applications commit with setSelection
or clearSelection. Pivot drill/filter/query and peer synchronization require
the grid plugin and are rejected here. Native navigation and legend visibility
callbacks update controller state. Geographic charts retain their separate
provider pack API, without controller/export parity.
The creation-time renderer is fixed. SVG is the default; an injected ECharts
renderer supports its documented three types and PNG, plus controller CSV.
capabilities intersects advertised export support with actual methods. Export
operations share the update queue and capture the committed state when their
turn begins. onBeforeDownload is synchronous; returning false cancels. Hooks
receive owned snapshots. Custom renderer failures trigger an update rollback;
if both fail the promise reports an AggregateError rather than claiming the
old scene survived. Optional methods fail clearly when unavailable.
The low-level renderer remains available for applications owning their own
state. Its update takes a complete render input, with caller-owned mutable data;
use fresh inputs and callbacks deliberately. The chart facade preserves root
module identities and does not promise a dependency-isolated bundle or SSR
rendering. DOM-free imports and data building are supported.
The chart entrypoint also exports the existing types used by these integrations:
PivotChartCsvOptions and PivotChartImageOptions for reusable export settings,
PivotChartNodeClick for callbacks, renderer theme/label/tooltip/PDF types, and
the ECharts, vector-PDF, and geographic provider contracts. Import them with
import type from @revolist/pivot/charts; the root package exports the same
types.
Render a chart outside the Pivot grid
Section titled “Render a chart outside the Pivot grid”This grid-linked API still requires a RevoGrid and Pivot plugins, even though
the chart container is outside the grid.
mountPivotChart() renders into any application-owned HTMLElement.
| Experience | Plugins | Mount option |
|---|---|---|
| Chart only | PivotPlugin + PivotChartsPlugin | Omit controls |
| Chart with your own controls | PivotPlugin + PivotChartsPlugin | Omit controls, then call chart.update() |
| Chart with built-in controls | Add PivotChartsUiPlugin | controls: 'standard' |
All three render in your container. None creates a popup.
Chart only
Section titled “Chart only”<revo-grid id="pivot"></revo-grid><div id="sales-chart" style="height: 420px"></div>import { PivotChartsPlugin, PivotPlugin, createPivotChartsRenderer, mountPivotChart,} from '@revolist/pivot';
const grid = document.querySelector<HTMLRevoGridElement>('#pivot')!;grid.pivotCharts = { renderer: createPivotChartsRenderer() };grid.plugins = [PivotPlugin, PivotChartsPlugin];
const chart = await mountPivotChart(grid, { container: document.querySelector<HTMLElement>('#sales-chart')!, presentation: { title: 'Sales by region' },});That is the complete integration. The chart defaults to grouped columns and stays linked to the committed Pivot result.
Chart with built-in controls
Section titled “Chart with built-in controls”Add PivotChartsUiPlugin, then request the standard controls at mount time:
import { PivotChartsPlugin, PivotChartsUiPlugin, PivotPlugin, mountPivotChart,} from '@revolist/pivot';
grid.plugins = [PivotPlugin, PivotChartsPlugin, PivotChartsUiPlugin];
const chart = await mountPivotChart(grid, { container: document.querySelector<HTMLElement>('#chart-workspace')!, controls: 'standard',});This embeds the same chart gallery, Data and Customize tabs, drill breadcrumbs, current-frame table, and export actions used by the standard dialog. The application still owns the outer container.
Chart with your own controls
Section titled “Chart with your own controls”Use the chart-only mount and connect only the controls your product needs:
const chart = await mountPivotChart(grid, { container });
typeSelect.addEventListener('change', () => { void chart.update({ chartType: typeSelect.value as PivotChartType, });});Use chart.update() for selected series, bindings, totals, link state,
presentation, theme, or chart-specific options. You do not need the UI plugin
for application-built controls.
Mount after the DOM exists
Section titled “Mount after the DOM exists”Mount and open charts
Compare a linked chart with a frozen snapshot as the Pivot changes, then relink it.
The container must exist when mountPivotChart() runs. In Vue, React, and
other component frameworks, mount the chart in the framework’s mounted effect
and destroy it during cleanup.
<script setup lang="ts">import { onBeforeUnmount, onMounted, ref } from 'vue';import { mountPivotChart, type PivotChartRef,} from '@revolist/pivot';
const grid = ref<HTMLRevoGridElement>();const container = ref<HTMLElement>();let chart: PivotChartRef | undefined;let disposed = false;
onMounted(async () => { const mounted = await mountPivotChart(grid.value!, { container: container.value!, }); if (disposed) mounted.destroy(); else chart = mounted;});
onBeforeUnmount(() => { disposed = true; chart?.destroy();});</script>
<template> <revo-grid ref="grid" /> <div ref="container" class="chart" /></template>
<style>.chart { height: 420px; }</style>Use the equivalent mounted effect for React. In an Astro portal, call
mountPivotChart() immediately after the portal target has been appended.
An existing container may start hidden and become visible later: the built-in
renderer uses ResizeObserver and redraws when its size changes. Give it a
non-zero height when visible. Do not place a container, selector, or ref in
grid.pivotCharts or grid.pivotChartsUi.
Open the modeless dialog
Section titled “Open the modeless dialog”PivotChartsUiPlugin.openChart() remains available for an application toolbar
or the Pivot Chart… context-menu action. A grid owns at most one open
dialog, but it may also own any number of embedded charts.
Right-clicking a Pivot analytical cell creates a cell-aware initial scope:
- a group value opens its immediate children;
- a leaf value opens its siblings under the same parent;
- subtotal and grand-total cells choose the nearest valid breakdown;
- the clicked column path and measure become the initial series;
- values-on-rows preserve the clicked row measure.
The scope comes from committed Pivot row and column metadata, not DOM text or a stale grid selection.
What stays synchronized
Section titled “What stays synchronized”Linked charts refresh after committed Pivot changes, filtering, sorting, group expansion, source replacement, and theme changes. Unlinking stores a bounded normalized dataset in the chart model; the snapshot then remains stable until the chart is relinked.
Use the Data tab to choose totals, relationship bindings, drill synchronization, and link state. Use Customize for presentation and chart-specific options. View data switches the plot to the accessible table for the current drill frame without mutating the Pivot or chart model.
Demo and API
Section titled “Demo and API”- Open the live Pivot demo for the complete analytical workspace. The screenshot states in this guide come from the registered Pivot Fields Panel example, which also runs in all four framework wrappers.
- Use the Pivot Charts API reference for exact generated interfaces and signatures.
- Continue with Chart types and compatibility.