Chart a spreadsheet
A live formula sheet wired to the built-in Chart panel: edit a Units or Revenue cell and the chart redraws. Customize it in-panel - change Type, swap Group by / Split by / Value, aggregate, Stack, or add data labels.
A live, editable Svelte 5 data grid example from the SvGrid gallery (Charts). See the SvGrid documentation for the full API.
About this example
A live formula sheet wired to the built-in chart panel of the Svelte 5 data grid. Type a new number into any Units or Revenue cell and the chart redraws on the spot, like a spreadsheet chart bound to a table, and the panel is the chart-design surface: switch the type between column, bar, line, area and pie, change Group by, Split by and Value, choose the aggregate, stack the series or turn on data labels, all live with no code. A clipboard context menu covers copy, cut, paste and clear.
A small editable sales sheet with the built-in chart drawer open beside it. Type a new number into any Units / Revenue cell and the chart redraws on the spot - exactly like an Excel chart bound to a table. The chart panel is the "Chart Design" surface: switch the chart Type (column / bar / line / area / pie), change what is on the axis (Group by / Split by / Value), aggregate, stack the series, or turn on data labels - all live, no code.
Imports, features and API used
Imports: @svgrid/grid
Columns: month (Month), region (Region), channel (Channel), units (Units), revenue (Revenue), cost (Cost)
Frequently asked questions
How does an edit reach the chart?
onCellValueChange writes the row, the grid's displayed rows change and the panel re-aggregates; nothing chart-specific is called.
Can users change the chart type themselves?
Yes. The panel's Type control is live, along with the axis pickers, aggregate, Stacked and Data labels, so end users design the chart without code.
Is the context menu related to charting?
No. contextMenu lists the clipboard commands for the editable cells; the chart panel is opened from the toolbar or the Chart selected range item.
Related documentation
Related articles
- A Fill Handle (Drag to Fill) in SvGrid - Build a working spreadsheet-style fill handle on top of SvGrid's cell selection and editing - pointer tracking, range highlighting, series fill, and undo/redo integration all covered.
- Pivot Tables in Svelte - Summarize Data Without a Spreadsheet - Run a cross-tab pivot directly inside your Svelte app using @svgrid/enterprise createPivotModel - no Excel, no server-side aggregation, no stale exports.
- Spreadsheet-Style Cell Range Selection in SvGrid - How to enable drag-to-select cell ranges, read live selection state, and build a status-bar footer that sums and averages the selected values.
Source code (356-spreadsheet-chart.svelte)
<script lang="ts">
/**
* 356. Spreadsheet chart (edit cells, customize the chart like Excel)
* ------------------------------------------------------------------
* A small editable sales sheet with the built-in chart drawer open beside it.
* Type a new number into any Units / Revenue cell and the chart redraws on the
* spot - exactly like an Excel chart bound to a table. The chart panel is the
* "Chart Design" surface: switch the chart Type (column / bar / line / area /
* pie), change what is on the axis (Group by / Split by / Value), aggregate,
* stack the series, or turn on data labels - all live, no code.
*/
import { SvGrid, tableFeatures, type GridColumns } from '@svgrid/grid'
type Row = { month: string; region: string; channel: string; units: number; revenue: number; cost: number }
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
const REGIONS = ['NA', 'EMEA']
const CHANNELS = ['Online', 'Retail', 'Wholesale']
// Seed a plausible ramp so the starting chart already looks like a real report.
const base: Record<string, number> = { Online: 42000, Retail: 31000, Wholesale: 58000 }
const seed: Row[] = []
MONTHS.forEach((month, m) => {
REGIONS.forEach((region, r) => {
CHANNELS.forEach((channel) => {
const revenue = Math.round((base[channel]! * (1 + m * 0.12) * (r === 0 ? 1 : 0.7)) / 500) * 500
seed.push({ month, region, channel, units: Math.round(revenue / 120), revenue, cost: Math.round(revenue * 0.62) })
})
})
})
let rows = $state<Row[]>(seed)
const features = tableFeatures({})
const money = { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } } as const
const columns: GridColumns<Row> = [
{ field: 'month', header: 'Month', width: 100 },
{ field: 'region', header: 'Region', width: 100 },
{ field: 'channel', header: 'Channel', width: 120 },
{ field: 'units', header: 'Units', width: 110, align: 'right', cellDataType: 'number', editorType: 'number', format: { type: 'number', options: { maximumFractionDigits: 0 } } },
{ field: 'revenue', header: 'Revenue', width: 130, align: 'right', cellDataType: 'number', editorType: 'number', format: money },
{ field: 'cost', header: 'Cost', width: 120, align: 'right', cellDataType: 'number', editorType: 'number', format: money },
]
const EDITABLE = new Set(['units', 'revenue', 'cost'])
function onCellValueChange(e: { rowIndex: number; columnId: string; newValue: unknown }) {
if (!EDITABLE.has(e.columnId)) return
const n = Number(e.newValue)
if (!Number.isFinite(n)) return
const next = rows.slice()
next[e.rowIndex] = { ...next[e.rowIndex]!, [e.columnId]: n }
rows = next
}
</script>
<div class="demo-page" style="height: 620px; display: flex; flex-direction: column;">
<p class="demo-lede">
An editable sales sheet with a live chart - like inserting a chart in Excel. Edit any
<strong>Units</strong> or <strong>Revenue</strong> cell and the chart redraws. Use the
<strong>Chart</strong> panel to customize it: change the <strong>Type</strong>, swap
<strong>Group by</strong> / <strong>Split by</strong> / <strong>Value</strong>, aggregate,
<strong>Stack</strong>, or add <strong>Labels</strong>.
</p>
<div style="flex: 1; min-height: 0;">
<SvGrid
columnResize
data={rows}
columns={columns}
features={features}
selectionMode="cell"
enableInlineEditing={true}
enableCellSelection={true}
containerHeight="100%"
contextMenu={['copy', 'cut', 'paste', 'clear']}
onCellValueChange={onCellValueChange}
charting={{
defaultOpen: true,
width: 500,
dimension: 'month',
series: 'channel',
measures: 'revenue',
defaultType: 'bar',
stacked: false,
}}
/>
</div>
</div>More Charts examples
- Chart a selection (context menu) - With integrated charting enabled, the right-click menu gains a Chart selected range item: select a block of cells, right-click, and the chart panel opens scoped to that range - the Excel chart-this gesture, built in. The item appears only when charting is on and is appended to the default context menu automatically.
- Chart view of the grid - The `chart` prop turns the same <SvGrid> into a chart, driven by the grid’s filtered + sorted rows (search + sort flow through). A view of the grid like board and scheduler, but the renderer is free: the grid lazy-loads a built-in view wrapping the standalone SvChart via rowsToChartSpec. Flip Table <-> Chart (bar / line / area) over one source of truth.
- Candlestick / OHLC - Candlestick and OHLC price marks with an ordinal date axis. ChartSeries.ohlc carries the four prices while values keeps the closes, so the CSV export, the screen-reader table and a 10-day moving average all work with no candle-specific code. Toggle the axis: a real time axis opens a gap over every weekend, an ordinal one spaces sessions evenly and still labels them by date. The strip under the plot is the chart brush: drag its window to pan, drag an edge to resize.
- Box plot + error bars - Distribution rather than average: box plots with the 1.5 IQR whisker rule and individual outliers, next to the same data as a bar chart of the means with error bars. two regions with nearly identical means sit side by side in the bars and look nothing alike in the boxes. boxStats() summarises a raw sample, rowsToBoxSpec() does it per group, and ChartSeries.errors annotates any existing mark without changing its type.
- Axes, titles and styling - The chart hands the decisions back: a numeric x axis that spreads payload sizes the way the numbers do, pinned domains with a fixed tick interval and a formatter per axis, grid lines on or off, a shaded budget band, a vertical reference line, per-point markers and colours, a stepped dashed series on the right axis, a title / subtitle / caption, a legend on any side, and a tooltip snippet in single-series mode. Below it, 50,000 monitoring readings decimated to one point per pixel with LTTB or min / max - toggle it off to see the cost.