Table
@domphy/table provides headless table logic for Domphy apps: sorting, filtering, pagination, row selection, grouping, expanding, pinning, column sizing, and faceting.
It is a 1-1 port of @tanstack/table-core v8.21.3 (MIT, © Tanner Linsley and the TanStack team). The source is kept byte-identical to upstream, so the entire TanStack Table v8 reference applies as-is, and future upstream versions can be diffed and merged directly.
Like the rest of Domphy, it is framework-agnostic and has zero dependencies — the bridge to the UI is plain toState.
Install
npm install @domphy/table
The CDN bundle exposes Domphy.table with all exports.
Live Example
Core Concepts
createTable— builds the table instance from yourdata,columns, row models, and state. The instance is the single API surface: header groups, row models, and every feature method hang off it.- Column defs — plain objects describing how to extract values (
accessorKey/accessorFn), what to render in headers/cells/footers, and per-column feature options.createColumnHelpergives you a typed builder. - Row models are opt-in — only
getCoreRowModel()is required. Sorting, filtering, pagination, grouping, expanding, and faceting each ship as a separateget*RowModel()factory you pass in explicitly, so unused features tree-shake away. - Headless —
@domphy/tablecomputes what to display; Domphy owns how. You map header groups and rows straight into plain element objects.
The Bridge Pattern
TIP
Most apps should use the Domphy adapter (createDomphyTable) — it packages the wiring below behind a reactive version, and the live example above already uses it. Read on to understand what the adapter does under the hood.
@domphy/table is state-driven: every interaction (sort click, filter input, page change) funnels through onStateChange. The bridge is one toState counter that bumps whenever table state changes — the UI reads it and re-renders from the instance:
import { createTable, getCoreRowModel, getSortedRowModel } from "@domphy/table"
import { toState } from "@domphy/core"
const tableVersion = toState(0)
const table = createTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
state: {},
onStateChange: (updater) => {
const next = typeof updater === "function" ? updater(table.getState()) : updater
table.setOptions((prev) => ({ ...prev, state: next }))
tableVersion.set(tableVersion.get() + 1)
},
renderFallbackValue: null,
})
table.setOptions((prev) => ({ ...prev, state: table.initialState }))
The UI touches tableVersion reactively, then reads everything else directly from the instance:
import { table as tableUI } from "@domphy/ui"
const App: DomphyElement<"table"> = {
table: (l) => {
tableVersion.get(l)
return [
{ thead: table.getHeaderGroups().map((headerGroup) => ({ tr: ..., _key: headerGroup.id })) },
{ tbody: table.getRowModel().rows.map((row) => ({ tr: ..., _key: row.id })) },
]
},
$: [tableUI()],
}
Note the import alias: the table() patch from @domphy/ui is imported as tableUI because table is taken by the instance. The docs use this aliasing throughout.
What To Read Next
- Domphy Adapter for
createDomphyTable— the recommended way to consume tables - Columns & Row Models for column defs,
createColumnHelper, and reading data out of the instance - Sorting & Filtering for sort toggling, column filters, and global filtering
- Pagination & Selection for page controls and row selection with checkboxes
- Advanced Features for grouping, expanding, visibility, ordering, pinning, sizing, and faceting
- API Reference for the full export list