Built-in charting: multi-series

The same charting prop, now multi-series: Group by Region, Split by Product, and toggle Stacked for a stacked / grouped chart. Change the pickers or filter a column and every series re-aggregates from the live grid rows.

A live, editable Svelte 5 data grid example from the SvGrid gallery (Charts). See the SvGrid documentation for the full API.

About this example

Multi-series charts from the Svelte 5 data grid's charting prop. Point the panel at a Split by column and it draws one series per distinct value, grouped or stacked with a Stacked toggle, over the Group by and Value pickers, so Region by Product revenue becomes grouped or stacked bars. Change the pickers or filter a column and every series re-aggregates from the live grid rows.

The charting prop isn't just one bar per group - point it at a series (split-by) column and it draws one series per distinct value, grouped or stacked. Everything is still one prop; the panel's Split by / Stacked / Value controls stay live, and filtering the grid re-aggregates the chart.

Imports, features and API used

Imports: @svgrid/grid

Table features registered: rowSortingFeature, columnFilteringFeature

Columns: region (Region), product (Product), quarter (Quarter), channel (Channel), revenue (Revenue), margin (Margin), units (Units)

Frequently asked questions

How do I get one series per product?

Choose Product in Split by. The panel groups the rows by Region, then by Product within each region, and aggregates Value per pair into a series.

What does Stacked do?

It stacks the series within each category instead of placing them side by side, and 100 percent mode normalises each stack to its total.

Can the split be set in code?

Yes. charting accepts series for the split-by column along with dimension and measures so the panel opens with the split already applied, and stacked sets the initial stacking.

Related documentation

Related articles

  • Sparkline Cells in a Svelte Data Grid - Show inline trend sparklines inside grid cells using SvGrid's built-in sparkline column property - no charting library needed, just a field that holds a number array.
  • Multi-Level (Grouped) Column Headers in SvGrid - Band related columns under a shared parent header using SvGrid's nested column definition - how to nest, pin, combine with sorting and filtering, and when NOT to use grouping.
  • How We Built Excel-Style Filters - How SvGrid implements per-column filter menus, type-aware operators, and a shared filter model that works identically for in-memory and server-side data.

Source code (354-charting-multi-series.svelte)

<!-- Documented in: docs/help/charts/from-the-grid.md -->
<script lang="ts">
  /**
   * 354. Built-in charting: split-by + stacked
   * ------------------------------------------
   * The `charting` prop isn't just one bar per group - point it at a `series`
   * (split-by) column and it draws one series per distinct value, grouped or
   * stacked. Everything is still one prop; the panel's Split by / Stacked /
   * Value controls stay live, and filtering the grid re-aggregates the chart.
   */
  import {
    SvGrid,
    tableFeatures,
    rowSortingFeature,
    columnFilteringFeature,
    type GridColumns,
  } from '@svgrid/grid'

  const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })

  type Sale = {
    id: number
    region: string
    product: string
    quarter: string
    channel: string
    revenue: number
    units: number
    margin: number
  }
  const REGIONS = ['Americas', 'EMEA', 'APAC']
  const PRODUCTS = ['PLC', 'Drivers', 'Rivets']
  const QUARTERS = ['Q1', 'Q2', 'Q3', 'Q4']
  const CHANNELS = ['Direct', 'Partner', 'Online']
  let seed = 0x5a1e5
  const rnd = () => ((seed = (seed * 1103515245 + 12345) >>> 0) / 0xffffffff)
  const rows: Sale[] = []
  let id = 0
  for (const region of REGIONS)
    for (const product of PRODUCTS)
      for (const quarter of QUARTERS) {
        const revenue = Math.round(20_000 + rnd() * 80_000)
        rows.push({
          id: id++,
          region,
          product,
          quarter,
          channel: CHANNELS[id % 3]!,
          revenue,
          units: Math.round(40 + rnd() * 560),
          margin: Math.round(revenue * (0.12 + rnd() * 0.28)),
        })
      }

  const money = { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } } as const
  const columns: GridColumns<Sale> = [
    { field: 'region', header: 'Region', width: 120 },
    { field: 'product', header: 'Product', width: 120 },
    { field: 'quarter', header: 'Quarter', width: 100 },
    { field: 'channel', header: 'Channel', width: 110 },
    { field: 'revenue', header: 'Revenue', width: 140, align: 'right', cellDataType: 'number', format: money },
    { field: 'margin', header: 'Margin', width: 130, align: 'right', cellDataType: 'number', format: money },
    { field: 'units', header: 'Units', width: 100, align: 'right', cellDataType: 'number' },
  ]
</script>

<div class="demo-page" style="height: 620px; display: flex; flex-direction: column;">
  <p class="demo-hint" style="margin: 0 0 8px;">
    One <code>charting</code> prop: <strong>Group by</strong> Region, <strong>Split by</strong> Product,
    <strong>Stacked</strong> - a multi-series chart. Change the pickers, toggle Stacked, or filter a column.
  </p>
  <div style="flex: 1; min-height: 0;">
    <SvGrid
      columnResize
      data={rows}
      {columns}
      {features}
      sortable
      filterable
      filterMode="row"
      selectionMode="both"
      containerHeight="100%"
      charting={{
        defaultOpen: true,
        width: 480,
        dimension: 'region',
        series: 'product',
        measures: 'revenue',
        stacked: true,
      }}
    />
  </div>
</div>

View this example on GitHub

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.