Data Table

Batteries-included table for admin list pages — search, filters, sorting, column visibility, pagination, loading skeletons and an empty state in one component. State lives outside the markup: call createDataTable({ rows, columns, rowKey }) (rows/columns must be getter functions so the table tracks them) and hand the returned controller in as table. Any column can be drawn by hand with a snippet named after its id{ id: "status" } is rendered by {#snippet status(row)}.

A full table

Search, filters, sorting, column visibility and paging, from one controller and a list of columns. Any list with those controls uses DataTable — hand-rolling from the raw Table primitives is how the same toolbar, empty row, skeleton and pagination end up copied across three screens. createDataTable is the only thing that talks to TanStack; app code never imports it.

Channel Status
3412

Ada Lovelace

ada@example.com

Webpaid3$248.902026-08-09
3411

Grace Hopper

grace@example.com

Instagramshipped1$89.002026-08-09
3410

Alan Turing

alan@example.com

Webrefunded2$132.502026-08-08
3409

Katherine Johnson

katherine@example.com

WhatsApppaid5$512.202026-08-08
3408

Margaret Hamilton

margaret@example.com

Webpending1$45.002026-08-07
Showing 1–5 of 12

Loading

loading renders skeletonRows placeholder rows, but only while the table has no rows yet — a table that already has data keeps it during a refetch instead of blinking back to bars. A column whose cell has a distinctive shape supplies its own <id>Skeleton snippet, so the placeholder row is the height of the row it is about to become.

Channel Status
No results

Empty

Say what is missing, in the table's own body rather than in place of the whole table — the toolbar has to stay reachable, or a reader who filtered themselves into nothing has no way back out.

Channel Status
No orders yet.
No results

API

Prop Type Default Description
table required DataTableController

Controller returned by createDataTable(...).

rowHref (row: *) => string

Turns each row into a navigable link to a detail page.

onRowClick (row: *) => void

Click handler for a row, for drawer or modal flows.

rowClass (row: *) => string

Per-row Tailwind classes, e.g. dimming a disabled merchant.

loading boolean false

Renders skeletonRows placeholder rows while true, but only when the table has no rows yet.

empty string 'No results found.'

Message shown in a full-width centered cell when there are no rows.

searchPlaceholder string

Placeholder for the toolbar search input. Omit to hide the search field entirely.

skeletonRows number 5

Number of skeleton rows rendered while loading.

filters boolean true

Renders the built-in Filters button whenever at least one column is filterable. Set to false to hide it.

filterOptions Object<string, Array<{value: *, label: string}>> {}

{ [columnId]: [{ value, label }] } option lists for value-picker columns, for server-driven tables.

filtersLoading boolean false

Replaces the Filters popover body with a spinner while option lists are being fetched.

onFiltersOpen () => void

Fires every time the Filters popover opens; the hook for lazy-loading filterOptions.

columnToggle boolean false

Shows the Columns popover listing every column not marked hideable: false.

resizable boolean false

Lets the user drag column edges; switches the table to table-fixed.

pageSizeOptions number[]

Row-count choices offered in the footer, e.g. [5, 10, 25, 50].

toolbar import('svelte').Snippet

Rendered in the left toolbar group, after search and Filters.

toolbarEnd import('svelte').Snippet

Rendered in the right toolbar group, before the column toggle.

class string

Additional Tailwind classes merged onto the root wrapper.

cells Object<string, import('svelte').Snippet<[*]>>

Rest props: snippets named after a column's id, e.g. {#snippet status(row)}, for custom cell rendering. A column whose cell has a distinctive shape (an avatar, a two-line title/subtitle, a badge) can also supply {#snippet <id>Skeleton()} — rendered in that column's cell while loading, in place of the generic bar, so the skeleton row's height matches the real row it's about to become.

DataTableColumn

Prop Type Default Description
id required string

Unique column id; also the snippet name for custom cell rendering.

accessor string|false

Dot-path into the row for the cell value; defaults to id. false marks a display-only column (e.g. actions).

label required string

Header text.

sortable boolean

Shows a sortable header button.

searchable boolean

Included in the toolbar search; defaults to true when there's an accessor.

filter boolean|'select'|'text'|'number'|'date'|'boolean'

Forces a filter editor type, or false to exclude the column from Filters.

options Array<{value: *, label: string}>

Explicit value list for a select filter, overriding derived facets.

align 'left'|'center'|'right'

Cell/header text alignment.

class string

Extra classes on each TableCell.

headClass string

Extra classes on the TableHead.

format (value: *, row: *) => string

Formats the raw value when no custom cell snippet is provided.

hideable boolean

Set to false to keep the column out of the Columns toggle and always visible.

width number

Column width in px, used when resizable is on.

minWidth number

Minimum drag width in px.

maxWidth number

Maximum drag width in px.

resizable boolean

Set to false to pin this column's width even when the table is resizable.

hidden boolean

Initially hidden.

DataTableQuery

Prop Type Default Description
search required string
filters required Object
sort required {column: string, direction: 'asc'|'desc'}|null
page required number
perPage required number

DataTableController

Prop Type Default Description
rows required Array<Object>

Visible (paginated/filtered/sorted) rows for the current page.

allColumns required Array<Object>

Every column, including hidden ones (for the Columns toggle).

headers required Array<Object>

Current page's header cells, for the table head row.

columns required Array<Object>

Resolved column defs (for colspan on skeleton/empty rows).

filterableColumns required Array<Object>

Columns eligible for the Filters panel.

activeFilters required Array<{id: string, column: Object, value: *}>

Currently applied filters, for the chips readout.

filters required Object

Draft/applied filter values keyed by column id.

dirty required boolean

True when search/filters/sort differ from initial state (shows the Reset button).

pending required boolean

True when commit: true and there are unapplied draft changes.

search required string

Bindable search box value.

commits required boolean

True when the table was created with commit: true.

page required number

Current 1-based page number (bindable).

perPage required number

Rows per page (bindable).

total required number

Total row count across all pages.

totalWidth required number

Sum of column widths, for the table's fixed-layout width when resizable.

query required DataTableQuery

Search/filters/sort/page state, for building a server request.

sortOf required (columnId: string) => ('asc'|'desc'|false)

Current sort direction for a column, if any.

toggleSort required (columnId: string) => void

Cycles a column's sort direction.

resizeColumn required (columnId: string, width: number) => void

Sets a column's width.

toggleColumn required (columnId: string) => void

Shows/hides a column.

clearFilter required (columnId: string) => void

Removes one active filter.

counts required (columnId: string) => Object

Facet value counts for a column's filter editor.

reset required () => void

Clears search, filters and sort back to initial state.

apply required () => void

Commits draft search/filters when commit: true.

Also exported from this module: createDataTable