Migrating from a UI-kit table (Flowbite / Skeleton / shadcn-svelte)
Flowbite-Svelte, Skeleton, and shadcn-svelte all ship a styled <Table>
component. They are presentation components: they render the rows you
pass and match your design tokens, but sorting, filtering,
virtualization, and editing are yours to write by hand. Moving to SvGrid
replaces that hand-rolled logic with built-in features - while still
letting you match the design system.
Not the shadcn-svelte data table. That one is a separate recipe built on TanStack Table v9, and it already does sorting, filtering, pagination, and row selection. If that is what you have, read Migrating from the shadcn-svelte data table instead - the port is different, and smaller.
Estimated effort: 30 min - 1 hour per table. You are mostly deleting
{#each}markup and the sort / filter state you wrote yourself.
When this guide applies
You have a table that started simple and has grown:
- You added a click-to-sort handler and a sort-state variable.
- You added a search box and an array
.filter(...). - The list got long and now you want virtualization or pagination.
- You started editing cells inline and it got messy.
That is the moment a styled <Table> stops paying for itself.
Before / after
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>
- <script>
- import { Table, TableHead, TableHeadCell, TableBody, TableBodyRow, TableBodyCell } from 'flowbite-svelte'
- let sortKey = 'name', asc = true
- $: sorted = [...rows].sort((a, b) => (a[sortKey] > b[sortKey] ? 1 : -1) * (asc ? 1 : -1))
- </script>
-
- <Table>
- <TableHead>
- <TableHeadCell on:click={() => (sortKey = 'name')}>Name</TableHeadCell>
- <TableHeadCell on:click={() => (sortKey = 'amount')}>Amount</TableHeadCell>
- </TableHead>
- <TableBody>
- {#each sorted as row}
- <TableBodyRow>
- <TableBodyCell>{row.name}</TableBodyCell>
- <TableBodyCell>{row.amount}</TableBodyCell>
- </TableBodyRow>
- {/each}
- </TableBody>
- </Table>
+ <script lang="ts">
+ import {
+ SvGrid, tableFeatures, rowSortingFeature, columnFilteringFeature,
+ type ColumnDef,
+ } from '@svgrid/grid'
+
+ const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
+ const columns: ColumnDef<typeof features, Row>[] = [
+ { field: 'name', header: 'Name' },
+ { field: 'amount', header: 'Amount', format: { type: 'currency', currency: 'USD' } },
+ ]
+ </script>
+
+ <SvGrid data={rows} columns={columns} features={features} showPagination />
The sort handler, the sorted derivation, and the {#each} all go
away. Sorting is a click on the header; filtering is the column menu.
Keep your design system
SvGrid is unstyled-by-token, so it can match Flowbite / Skeleton / shadcn:
- Re-theme through the
--sg-*CSS custom properties (borders, header background, zebra rows, hover, selection, density). - Or drive those tokens from your Tailwind theme - see the Tailwind integration guide.
What you get for free
- Sorting, filtering, and pagination without writing handlers.
- Row + column virtualization so thousands of rows stay smooth.
- Inline editing, cell-range selection + copy, grouping, tree, and master/detail when you need them.
- Accessibility - WAI-ARIA grid roles and keyboard navigation built
in, instead of a plain
<table>.
What you give up
- The tiny footprint of a styled
<table>for a truly static list. - A pixel-perfect, zero-config match with the rest of the kit - you re-create the look with tokens instead (a one-time setup).
Frequently asked questions
When should I replace a Flowbite / Skeleton / shadcn table with a data grid?
When the table needs sorting, filtering, virtualization, inline editing, or
large datasets. These styled <Table> components are presentation only;
once you are hand-writing table behaviour, a data grid like SvGrid removes
that code. (shadcn-svelte also ships a separate TanStack-based data table -
see that migration guide.)
Can SvGrid match my existing UI-kit styling?
Yes. SvGrid themes through --sg-* CSS variables, which you can wire to your
Tailwind / design-system tokens so it matches Flowbite, Skeleton, or
shadcn-svelte.
Is it overkill for a small static table?
For a small, static table that never sorts or filters, a styled <table> is
lighter and fine. Switch when the behaviour - not just the styling - becomes the
work.
What you end up with
Everything the presentational table could not do: sort, filter, page, edit.
<SvGrid data={rows} {columns} sortable filterable pageable pageSize={3} editable />
See also
- SvGrid vs UI-kit tables - the side-by-side comparison
- Tailwind integration - match your design system
- Getting started - a working grid in ~15 lines
Related articles
- Migrating a Svelte 4 Table Component to Svelte 5 - A concept-by-concept guide to porting a hand-rolled Svelte 4 data table to Svelte 5 runes - props, reactivity, stores, slots, events, and when to stop hand-rolling entirely.
- Migrating from DataTables.net to a Svelte Data Grid - A practical migration guide from jQuery DataTables to SvGrid - how column definitions, server-side data, custom rendering, and selection map across, with working code for each step.
- Migrating from ag-grid-react to a Svelte Stack - A practical field guide for porting ag-grid-react screens to SvGrid - column defs, cell renderers, server-side data, and the React-to-Svelte reactivity shift.