Table
@domphy/table provides headless table logic for Domphy apps: sorting, filtering, pagination, row selection, grouping, expanding, pinning, column sizing, and faceting.
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<script src="https://unpkg.com/@domphy/table/dist/table.global.js"></script>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