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">
/**
* 484. 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[][] = [
['Product', 'Price', 'Source', '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} />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).