Row data
Row data is whatever you pass to <SvGrid data={...}>. It is a
ReadonlyArray<TData> where TData is your row type. Any shape works;
the grid does not require a base class or interface.
Open the live example: Quick start (Getting Started)
Static
<script lang="ts">
const rows = [
{ id: '1', name: 'Ada', age: 36 },
{ id: '2', name: 'Linus', age: 54 },
]
</script>
<SvGrid data={rows} {columns} features={{}} />
Reactive
Use a Svelte 5 $state array. The grid re-derives its row model whenever
the array reference changes:
<script lang="ts">
let rows = $state<Person[]>([])
$effect(() => { fetchPeople().then((next) => (rows = next)) })
</script>
<SvGrid data={rows} {columns} features={{}} />
In-place mutation works too - rows.push(x) or rows[i] = y - because
$state arrays are deep-reactive.
Row identity (getRowId)
Without getRowId, the grid uses each row's array index as its id.
That's fine for static data, but selection / expansion / edit state
won't survive sorts, filters, or insertions.
For anything beyond a read-only grid, pass getRowId:
<SvGrid
data={rows}
{columns}
features={features}
getRowId={(row, index) => row.id}
/>
The function fires per row at row-model build time. Return any stable
string - a database PK, a UUID, a slug. The same id then flows into
onRowSelectionChange, onCellValueChange, api.getCellValue(...),
and every other API surface that takes a row id.
Available on both the <SvGrid> wrapper and the headless
createSvGrid({ getRowId }) core.
Empty state
The grid renders the emptyMessage prop when data.length === 0:
<SvGrid data={[]} {columns} features={{}} emptyMessage="No people found." />
Loading state
Pass loading to overlay a spinner / skeleton (the wrapper has a built-in
overlay layer):
<SvGrid {data} {columns} features={{}} loading={isFetching} />
For controlled skeleton-row UX in virtualized server-side grids, see demos/09-server-side.svelte.
Replacing the array
Assigning a new array is the ordinary way to load data, and the grid re-derives its row model from the new reference. Sort or filter state survives, because neither is stored on the rows.
<script lang="ts">
import { SvGrid, type GridColumns } from '@svgrid/grid'
type Person = { id: number; name: string; city: string; salary: number }
const seed: Person[] = [
{ id: 1, name: 'Ada Lovelace', city: 'London', salary: 142000 },
{ id: 2, name: 'Grace Hopper', city: 'New York', salary: 168000 },
{ id: 3, name: 'Linus Torvalds', city: 'Portland', salary: 155000 },
{ id: 4, name: 'Radia Perlman', city: 'Seattle', salary: 161000 },
]
let rows = $state<Person[]>([])
let status = $state('empty')
const columns: GridColumns<Person> = [
{ field: 'name', header: 'Name', width: 180 },
{ field: 'city', header: 'City', width: 150 },
{ field: 'salary', header: 'Salary', width: 140,
format: { type: 'currency', currency: 'USD' } },
]
// Stands in for a fetch. The grid never sees the difference.
async function load() {
status = 'loading'
await new Promise((r) => setTimeout(r, 350))
rows = seed.map((p) => ({ ...p }))
status = rows.length + ' rows'
}
// Load once on mount, the way a page would.
$effect(() => {
load()
})
</script>
<button type="button" onclick={load}>Load</button>
<button type="button" onclick={() => { rows = []; status = 'empty' }}>Clear</button>
<span>{status}</span>
<SvGrid data={rows} {columns} sortable />
What getRowId buys you
Select a row in each grid, then sort by salary. The left grid keys rows by
array position, so the selection follows the slot rather than the person; the
right one keys by id and keeps hold of who you picked.
<script lang="ts">
import { SvGrid, type GridColumns } from '@svgrid/grid'
type Person = { id: number; name: string; city: string; salary: number }
const seed: Person[] = [
{ id: 1, name: 'Ada Lovelace', city: 'London', salary: 142000 },
{ id: 2, name: 'Grace Hopper', city: 'New York', salary: 168000 },
{ id: 3, name: 'Linus Torvalds', city: 'Portland', salary: 155000 },
{ id: 4, name: 'Radia Perlman', city: 'Seattle', salary: 161000 },
]
const columns: GridColumns<Person> = [
{ field: 'name', header: 'Name', width: 160 },
{ field: 'salary', header: 'Salary', width: 130,
format: { type: 'currency', currency: 'USD' } },
]
</script>
<p>No <code>getRowId</code> - selection is tied to the row index:</p>
<SvGrid data={seed} {columns} sortable selectionMode="row" />
<p>With <code>getRowId</code> - selection is tied to the person:</p>
<SvGrid data={seed} {columns} sortable selectionMode="row" getRowId={(r) => String(r.id)} />
See also
Live examples
- Quick start - A realistic 25-row × 9-column grid with sort, filter, selection, inline editing, and column resize all enabled.
- Server-side data - Sort/filter/page round-tripped to a mock endpoint with debounce + cancel.