IMAGE: a picture inside the cell

Excel's IMAGE puts a picture IN a cell rather than floating one over it, so it sorts with its row, filters with it, copies as a formula and moves when the cells move. A product list whose thumbnail column reads the swatch beside it: sort by price and the pictures follow their rows. The source is a web address or a data URL, anything else stays text rather than becoming a broken image, and the second argument is the alt text a screen reader reads. (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

Excel's IMAGE function in a Svelte 5 spreadsheet: a picture IN a cell rather than floating over one, so it sorts with its row, filters with it, copies as a formula and moves when the cells move. A product list with a thumbnail column reading the swatch beside it. The source is a web address or a data URL, anything else stays text rather than becoming a broken image, and the second argument is the alt text. The formula goes into the .xlsx as _xlfn.IMAGE, where Excel keeps it. Enterprise, in @svgrid/enterprise.

Excel's IMAGE puts a picture IN a cell rather than floating one over it, and the difference is the whole point.

=IMAGE(source, [alt]) the cell IS the picture. It sorts with its row, filters with it, copies as a formula, and moves when the cells move. Insert > Picture the other kind: an object anchored to a cell, floating over whatever is under it.

A product list is the case this exists for. The thumbnail column holds a formula reading the swatch beside it, so a row sorted to the top takes its picture along and nothing has to be kept in step.

The source is a web address or a data URL; anything else stays text rather than becoming a broken image. The second argument is the alt text, worked out like any other argument.

Try: sort by Price with Data > Sort and watch the pictures follow their rows. Type =IMAGE(C2) into an empty cell for a copy of the first one. Save As: the formula goes into the .xlsx as Excel stores it.

Imports, features and API used

Imports: @svgrid/enterprise

Frequently asked questions

How is this different from Insert > Picture?

Insert > Picture anchors an object that floats over the cells. An IMAGE cell is the picture: it sorts with its row, filters with it, copies as a formula and moves when the cells move, with nothing to keep in step. Use the object for a logo on the sheet and IMAGE for a column of thumbnails.

What can the source be?

A web address or a data URL. Anything else, a file path or a javascript: scheme, is left as text rather than drawn, because a cell showing its source says more than a broken image does. The source can be a reference or a formula, so a thumbnail column can read a URL column beside it.

Does a screen reader learn what the picture is?

Yes, from the second argument, which is worked out like any other argument and becomes the image's alt text. Without it the picture has an empty alt, which is right for a decorative swatch and wrong for a picture that carries meaning, so give it one.

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.
  • Avatar and Image Cells in SvGrid - How to render avatars, product thumbnails, and logos in grid cells with proper fallbacks, lazy loading, and accessible alt text using Svelte 5 snippets.
  • 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 (494-sheet-cell-images.svelte)

<script lang="ts">
  /**
   * 494. IMAGE: a picture inside the cell
   * -------------------------------------
   * Excel's `IMAGE` puts a picture IN a cell rather than floating one over
   * it, and the difference is the whole point.
   *
   *   =IMAGE(source, [alt])   the cell IS the picture. It sorts with its
   *                           row, filters with it, copies as a formula,
   *                           and moves when the cells move.
   *   Insert > Picture        the other kind: an object anchored to a cell,
   *                           floating over whatever is under it.
   *
   * A product list is the case this exists for. The thumbnail column holds
   * a formula reading the swatch beside it, so a row sorted to the top
   * takes its picture along and nothing has to be kept in step.
   *
   * The source is a web address or a data URL; anything else stays text
   * rather than becoming a broken image. The second argument is the alt
   * text, worked out like any other argument.
   *
   * Try: sort by Price with Data > Sort and watch the pictures follow their
   * rows. Type =IMAGE(C2) into an empty cell for a copy of the first one.
   * Save As: the formula goes into the .xlsx as Excel stores it.
   */
  import { SvSheet, createWorkbook, createSheetDocument } from '@svgrid/enterprise'

  /** Four flat SVG swatches as data URLs, so the demo needs no network. */
  const swatch = (fill: string, mark: string): string =>
    'data:image/svg+xml;base64,' + btoa(
      `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 32"><rect width="48" height="32" rx="4" fill="${fill}"/>`
      + `<text x="24" y="21" font-family="sans-serif" font-size="13" fill="#fff" text-anchor="middle">${mark}</text></svg>`,
    )

  const products = [
    ['Licence', '1200', swatch('#2563eb', 'L'), 'A blue licence swatch'],
    ['Support', '480', swatch('#16a34a', 'S'), 'A green support swatch'],
    ['Training', '950', swatch('#ea580c', 'T'), 'An orange training swatch'],
    ['Hosting', '260', swatch('#a855f7', 'H'), 'A purple hosting swatch'],
  ]

  const rows: string[][] = [
    // "Source (data URL)": the column is text on purpose, the address the
    // thumbnail reads, and the header says so before the truncated
    // `data:image/...` reads as a picture that failed to draw.
    ['Product', 'Price', 'Source (data URL)', 'Alt text', 'Thumbnail'],
    ...products.map((p, i) => [...p, `=IMAGE(C${i + 2}, D${i + 2})`]),
    ['', '', '', '', ''],
    ['Not an image source', '', 'ftp://example.com/a.png', 'Left as text', '=IMAGE(C7, D7)'],
  ]

  const wb = createWorkbook([{ name: 'Catalogue', cells: rows }])
  const doc = createSheetDocument({ workbook: wb })
  const sheet = doc.get('Catalogue')
  const at = { rowIdAt: (i: number) => `r${i}`, columnIdAt: (i: number) => String.fromCharCode(65 + i) }
  sheet.formats.set([[0, 0, 0, 4]], { bold: true, fill: '#e2e8f0', color: '#0f172a' }, at)
  sheet.formats.set([[1, 1, 4, 1]], { numFmt: '$#,##0' }, at)
  sheet.formats.set([[1, 2, 7, 3]], { color: '#64748b' }, at)
  sheet.widths.A = 130
  sheet.widths.C = 150
  sheet.widths.D = 190
  sheet.widths.E = 110
  // Room for a picture: Excel's IMAGE fits the cell, so the cell decides.
  for (let r = 1; r <= 4; r += 1) sheet.heights.set(r, 40)
  sheet.freeze = { rows: 1, cols: 1 }
  sheet.autoFilter = { range: [0, 0, 4, 4], filters: {} }
</script>

<SvSheet document={doc} height="100%" rows={14} columns={7} />

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.