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, and a picture Excel cannot keep as it is, an SVG say, is drawn to a PNG when it is inserted so the file can carry it. 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; Save As says so in the status bar.

Related documentation

Related articles

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

<script lang="ts">
  /**
   * 485. 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.
  // Anchored from column B on: column A is frozen, and a frozen column
  // paints over whatever scrolls under it, as a frozen pane does in Excel.
  const charts: SheetObject[] = [
    {
      id: objectId(),
      kind: 'chart',
      anchor: { row: 6, col: 1, dx: 8, dy: 8, width: 400, 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: 5, dx: 32, dy: 8, width: 400, 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.
  • Blank sheet - just type - An empty Excel-style sheet on a plain <SvGrid>: column-letter headers (A..Z), a built-in 1..N row gutter, a name box + formula bar with a browsable function picker, gridlines, range selection and a fill handle. A real HyperFormula engine underneath: type a literal or a formula like =SUM(B2:D2) / =IF(...) and every dependent cell recalculates live. Drag a row or column border to resize; right-click for Cut / Copy / Paste / Clear.
  • Excel keyboard shortcuts - The muscle memory a spreadsheet user arrives with: Ctrl+Arrow jumps to the edge of the data region (a run-boundary search, so it hops gaps rather than running to the end), Ctrl+Shift+Arrow extends there, Ctrl+A takes the current region then the sheet, Ctrl+Space / Shift+Space take the column or row, Ctrl+D / Ctrl+R fill, Ctrl+; stamps the date. One enableSheet() call. Ctrl+Z undoes a whole fill in one press, not one per cell.
  • Formula bar + cell formats - The Excel cell experience: a formula bar showing the RAW text behind the active cell (the grid shows 1,234.50, the bar shows =B2*C2) with function autocomplete and signature hints, a Name Box that jumps to an address, and number formats that live on the CELL rather than the column. Ctrl+Shift+4 for currency, Ctrl+B to bold. The format store keys on row id, so sorting does not leave formatting behind on the old index.