Charts and pictures on the sheet

Charts anchored over the cells the way Excel anchors one: each reads a range rather than a copy of the numbers, so typing into a cell redraws it. Insert > Chart charts the selected block and reads its first row and column as the labels, Insert > Picture puts an image on the sheet, and a double-click opens the Chart dialog for the type, the title, series in columns or rows and stacking. Drag an object to move it, its corner to resize, Delete to remove. It hangs from a cell, so inserting a row above moves it, and it rides in getState() and in the .xlsx as a real drawing part. (requires @svgrid/enterprise)

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

About this example

Charts anchored over the cells of a Svelte 5 spreadsheet, the way Excel anchors one: each reads a range rather than a copy of the numbers, so typing into a cell redraws it. Insert > Chart charts the selected block and reads its first row and column as the labels; Insert > Picture puts an image on the sheet; a double-click opens the Chart dialog for the type, the title, series in columns or rows and stacking. Drag an object to move it, its corner to resize, Delete to remove; it hangs from a cell, so inserting a row above moves it. The charts are part of the document and ride through getState(). Enterprise, in @svgrid/enterprise.

A quarterly sales sheet with two charts anchored over it, the way Excel anchors one: each reads a RANGE rather than a copy of the numbers, so typing into a cell redraws it on the spot.

Insert > Chart charts the selected block. The first row and column are read as the labels, each column is a series, and the chart lands under the block. Insert > Picture puts an image on the sheet from a file. Insert > Setup (or a double-click on a chart) opens the Chart dialog: type, title, series in columns or rows, whether the first row and column are labels, and stacking.

An object floats over the cells: drag it to move, its corner to resize, Delete to remove. It hangs from a cell and an offset inside it, so inserting a row above moves it and deleting that row takes it with it. Everything here is part of the document: getState() carries the charts with the cells, and onChange reports objects. They go into the .xlsx too, as Excel's own drawing part, a chart carrying the references its series read rather than a copy of the numbers.

Try: change a number in B2:E4 and watch both charts. Select A1:E4 and press Insert > Chart for a third. Double-click the line chart and make it an area chart. Insert a row above row 1 and watch the charts move with their cells. Save As and open the file in Excel: the charts are charts there too.

Imports, features and API used

Imports: @svgrid/enterprise

Frequently asked questions

Does the chart update when a cell changes?

Yes. A chart object holds the range it reads, not a copy of the values, so every edit inside that range redraws it, the same way an Excel chart bound to a table does.

Where do the charts live?

In the document, per sheet: getState().sheets.Sales.objects. Each carries its anchor cell, an offset inside it, a size in pixels, the range it reads and how to read it. They move with an insert or delete and come back from a saved document.

Do charts ride in the .xlsx?

Yes, both ways. A chart goes out as a chart part of its own, carrying the references its series read rather than a copy of the numbers, so Excel redraws it from the cells beside it; a picture goes out as bytes in xl/media. Both are anchored by a drawing part, and a file opened here gives them back. The one exception is a picture whose source is a URL rather than a data URL, whose bytes are not in the document to write.

Related documentation

Related articles

Source code (485-sheet-charts-objects.svelte)

<script lang="ts">
  /**
   * 475. Charts and pictures on the sheet
   * -------------------------------------
   * A quarterly sales sheet with two charts anchored over it, the way
   * Excel anchors one: each reads a RANGE rather than a copy of the
   * numbers, so typing into a cell redraws it on the spot.
   *
   *   Insert > Chart    charts the selected block. The first row and
   *                     column are read as the labels, each column is a
   *                     series, and the chart lands under the block.
   *   Insert > Picture  puts an image on the sheet from a file.
   *   Insert > Setup    (or a double-click on a chart) opens the Chart
   *                     dialog: type, title, series in columns or rows,
   *                     whether the first row and column are labels, and
   *                     stacking.
   *
   * An object floats over the cells: drag it to move, its corner to
   * resize, Delete to remove. It hangs from a cell and an offset inside
   * it, so inserting a row above moves it and deleting that row takes it
   * with it. Everything here is part of the document: `getState()`
   * carries the charts with the cells, and `onChange` reports `objects`.
   * They go into the .xlsx too, as Excel's own drawing part, a chart
   * carrying the references its series read rather than a copy of the
   * numbers.
   *
   * Try: change a number in B2:E4 and watch both charts. Select A1:E4 and
   * press Insert > Chart for a third. Double-click the line chart and
   * make it an area chart. Insert a row above row 1 and watch the charts
   * move with their cells. Save As and open the file in Excel: the charts
   * are charts there too.
   */
  import { SvSheet, createWorkbook, createSheetDocument, objectId, type SheetObject } from '@svgrid/enterprise'

  const rows: string[][] = [
    ['Region', 'Q1', 'Q2', 'Q3', 'Q4', 'Year'],
    ['North', '48000', '52500', '61000', '68500', '=SUM(B2:E2)'],
    ['South', '39000', '41500', '44000', '52000', '=SUM(B3:E3)'],
    ['EMEA', '71000', '69500', '78000', '91000', '=SUM(B4:E4)'],
    ['Total', '=SUM(B2:B4)', '=SUM(C2:C4)', '=SUM(D2:D4)', '=SUM(E2:E4)', '=SUM(F2:F4)'],
  ]

  const wb = createWorkbook([{ name: 'Sales', cells: rows }])
  const doc = createSheetDocument({ workbook: wb })
  const sheet = doc.get('Sales')
  const at = { rowIdAt: (i: number) => `r${i}`, columnIdAt: (i: number) => String.fromCharCode(65 + i) }
  sheet.formats.set([[0, 0, 0, 5]], { bold: true, fill: '#e2e8f0', color: '#0f172a' }, at)
  sheet.formats.set([[4, 0, 4, 5]], { bold: true, border: { top: { width: 1 } } }, at)
  sheet.formats.set([[1, 1, 4, 5]], { numFmt: '$#,##0' }, at)
  sheet.widths.A = 110
  sheet.freeze = { rows: 1, cols: 1 }

  // Two charts over the same numbers, read two ways: the quarters by
  // region, and the regions by quarter. Both are part of the document.
  const charts: SheetObject[] = [
    {
      id: objectId(),
      kind: 'chart',
      anchor: { row: 6, col: 0, dx: 8, dy: 8, width: 430, height: 250 },
      range: [0, 0, 3, 4],
      type: 'bar',
      headers: true,
      series: 'columns',
      title: 'Quarters by region',
    },
    {
      id: objectId(),
      kind: 'chart',
      anchor: { row: 6, col: 4, dx: 24, dy: 8, width: 430, height: 250 },
      range: [0, 0, 3, 4],
      type: 'line',
      headers: true,
      series: 'rows',
      title: 'Regions by quarter',
    },
  ]
  sheet.objects = charts
</script>

<SvSheet document={doc} height="100%" rows={22} columns={8} />

View this example on GitHub

More Spreadsheet examples

  • Spreadsheet + Ribbon bar - The whole Excel surface as one component, <SvSheet workbook={wb} />: a six-tab ribbon (Home, Insert, Formulas, Data, Review, View), the Name Box and fx bar, sheet tabs and the Sum / Average / Count status bar. Format cells, merge them, comment on them, validate what goes in, colour them by rule, filter the region, protect the sheet; every button is the same call as its shortcut, so Ctrl+B and the Bold button cannot drift. A two-sheet P&L with the formats travelling in the document.
  • Spreadsheet + formulas - Real formula engine inside the grid: cell refs (A1), ranges (A1:A10), SUM / AVG / IF / COUNTIF / ROUND, arithmetic, string concat, cycle detection.
  • Per-cell custom borders (KPI) - Editable KPI scorecard. spreadsheetLayout paints spreadsheet-style per-edge custom borders via an absolute-positioned overlay (no border-collapse conflicts). Edit any quarter or target - the borders re-derive: green double = beat target, blue solid = hit, amber dotted = near miss, red dashed = bad miss; row champion gets a colored full frame.
  • Cell merging (spreadsheet shell) - A real invoice rendered on an Excel-style shell: A / B / C / D / E column letters across the top, row numbers down the left. Brand band, bill-from / bill-to address blocks, meta block, line items, totals, notes, signatures - all assembled from MergeSpec + CellBorderSpec. Editable Qty / Rate / addresses / notes; totals recompute live.
  • HyperFormula integration - Full HyperFormula engine wired into the grid as a peer-optional dep. Editable spreadsheet with A1-style cell refs, dozens of formulas across math (SUM / SUMIF), lookup (VLOOKUP / INDEX-MATCH), text (CONCAT / UPPER), date (TODAY / DATEDIF), logical (IF nests), financial (PMT / IRR / NPV), statistical (AVERAGE / MAX / RANK).