Migrating from TanStack Table

TanStack Table (formerly React Table v8) is the closest conceptual sibling to sv-grid - both are "headless data grid" libraries with explicit row-model pipelines. If you already think in createTable() + getCoreRowModel(), this is a half-day port.

Estimated effort: 30 min per grid for read-only views, 2-4 hours for editing-heavy grids.

On v9? SvGrid exports the same feature vocabulary, so most of this page is automated. Run npx @svgrid/migrate src to preview the port, and see Migrating from the shadcn-svelte data table if that is where your table came from.

Vocabulary cheat sheet

TanStack Table sv-grid
useReactTable({...}) createSvGrid({...}) or <SvGrid>
getCoreRowModel() createCoreRowModel()
getSortedRowModel() Auto-registered by rowSortingFeature
getFilteredRowModel() Auto-registered by columnFilteringFeature
getPaginationRowModel() Built in; toggle with showPagination
getGroupedRowModel() columnGroupingFeature + api.setGroupBy()
getExpandedRowModel() rowExpandingFeature
flexRender(col.cell, ctx) renderSnippet(MyCell, props) OR string field
columnHelper.accessor('field') { field: 'field' } directly
table.getRowModel().rows api.getDisplayedRows()
table.setColumnFilters(...) api.setFilter(id, {...}) per column
meta (column / table) n/a - use column cellClass / cell instead

Imports

The example at the end of this page runs against these rows:

<script lang="ts">
  import { SvGrid, type GridColumns } from '@svgrid/grid'

  type Person = {
    id: number
    name: string
    department: string
    city: string
    age: number
    salary: number
  }

  const people: Person[] = [
    { id: 1, name: 'Ada Lovelace',   department: 'Engineering', city: 'London',   age: 36, salary: 142000 },
    { id: 2, name: 'Grace Hopper',   department: 'Engineering', city: 'New York', age: 45, salary: 168000 },
    { id: 3, name: 'Linus Torvalds', department: 'Platform',    city: 'Portland', age: 54, salary: 155000 },
    { id: 4, name: 'Radia Perlman',  department: 'Networking',  city: 'Seattle',  age: 49, salary: 161000 },
    { id: 5, name: 'Barbara Liskov', department: 'Platform',    city: 'Boston',   age: 52, salary: 172000 },
  ]

  let rows = $state<Person[]>(people)

  const columns: GridColumns<Person> = [
    { field: 'name',       header: 'Name',       width: 200, editorType: 'text' },
    { field: 'department', header: 'Department', width: 150, editorType: 'text' },
    { field: 'city',       header: 'City',       width: 140, editorType: 'text' },
    { field: 'age',        header: 'Age',        width: 90,  editorType: 'number' },
    { field: 'salary',     header: 'Salary',     width: 130, editorType: 'number', format: { type: 'currency', currency: 'USD' } },
  ]
</script>
- import {
-   useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel,
-   flexRender, createColumnHelper,
- } from '@tanstack/react-table'

+ import {
+   SvGrid, tableFeatures, rowSortingFeature, columnFilteringFeature,
+   renderSnippet,
+ } from '@svgrid/grid'

Column definitions

- const columnHelper = createColumnHelper<Order>()
- const columns = [
-   columnHelper.accessor('orderId',  { header: 'Order ID' }),
-   columnHelper.accessor('amount',   { header: 'Amount', cell: ({ getValue }) => fmt(getValue<number>()) }),
- ]

+ const columns: ColumnDef<typeof features, Order>[] = [
+   { field: 'orderId', header: 'Order ID' },
+   { field: 'amount',  header: 'Amount', format: { type: 'currency', currency: 'USD' } },
+ ]

The fieldFn form is the same:

- columnHelper.accessor((row) => row.profile.name, { id: 'name', header: 'Name' })

+ { id: 'name', fieldFn: (row) => row.profile.name, header: 'Name' }

Mounting

- const table = useReactTable({
-   data, columns,
-   getCoreRowModel:       getCoreRowModel(),
-   getSortedRowModel:     getSortedRowModel(),
-   getFilteredRowModel:   getFilteredRowModel(),
- })
- return (
-   <table>
-     <thead>{table.getHeaderGroups().map(/* ... */)}</thead>
-     <tbody>{table.getRowModel().rows.map(/* ... */)}</tbody>
-   </table>
- )

+ const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
+
+ <SvGrid data={rows} columns={columns} features={features} />

Sv-grid bundles the render + virtualization. No <table> you have to author yourself.

Imperative state

TanStack Table sv-grid
table.setColumnFilters([{id, value}]) api.setFilter(id, { operator: 'contains', value })
table.getColumn('id')?.setFilterValue(v) api.setFilter(id, { operator: 'contains', value: v })
table.setSorting([{id, desc}]) api.setSort(id, desc ? 'desc' : 'asc')
table.setRowSelection(state) n/a - use api.clearRowSelection() + UI events
table.toggleAllRowsExpanded() api.expandAllGroups()
table.getColumn('x').toggleVisibility() api.setColumnVisible('x', false)
table.options.meta?.foo Pass through component props / $state

Custom cells

- {
-   header: 'Status',
-   cell: ({ row }) => <Badge tone={row.original.status} />,
- }

+ {
+   field: 'status', header: 'Status',
+   cell: (ctx) => renderSnippet(StatusCell, { tone: ctx.row.original.status }),
+ }

renderSnippet accepts any Svelte 5 snippet; renderComponent accepts a regular component if you prefer SFCs.

Server-side data

- const [pagination, setPagination] = useState({pageIndex: 0, pageSize: 25})
- const [sorting, setSorting]       = useState([])
- const table = useReactTable({
-   data, columns, manualPagination: true, manualSorting: true,
-   state: { pagination, sorting },
-   onPaginationChange: setPagination,
-   onSortingChange:    setSorting,
- })

+ <SvGrid
+   data={rows} columns={columns} features={features}
+   externalSort={true}
+   externalFilter={true}
+   onSortingChange={(clauses) => refetch({ sort: clauses })}
+   onFiltersChange={(f)       => refetch({ filters: f.columns })}
+ />

What you get for free vs TanStack Table

What you give up

Frequently asked questions

How long does it take to migrate from TanStack Table to SvGrid?

About 30 minutes per read-only grid and 2-4 hours for editing-heavy grids. Both libraries use explicit row-model pipelines, so createTable() + getCoreRowModel() maps almost directly onto createSvGrid() + createCoreRowModel().

Why switch from TanStack Table to SvGrid on Svelte 5?

TanStack Table is headless-only and multi-framework, so you build the entire DOM, virtualization, filter UI, and editing layer yourself. SvGrid keeps the headless core but ships a batteries-included <SvGrid> render component - virtualization, Excel-style filters, cell selection, and inline editing work in one prop pass.

Is SvGrid free like TanStack Table?

Yes - @svgrid/grid is MIT-licensed, like TanStack Table. SvGrid additionally offers an optional paid @svgrid/enterprise pack for export, pivot, and import, which TanStack Table does not provide at all.

What you end up with

The same model you had, with the rendering already written.

<SvGrid data={rows} {columns} sortable filterable pageable pageSize={3} selectable />

See also

Related articles