Composition recipe
Start with native table semantics, then add TanStack Table features as the product needs them.
npx shadcn@4.19.0 add https://ui.edbn.me/r/data-table.jsonPackages1
Registry items5
Use the Table primitives for fixed markup. Install the Data Table starter when rows need sorting, filtering, pagination, selection, or controlled product state. There is intentionally no universal data table component.
components/data-table/core/
├── data-table.tsx
├── data-table-features.ts
├── data-table-column-header.tsx
├── data-table-pagination.tsx
├── data-table-toolbar.tsx
└── data-table-view-options.tsxColumn definitions and product data stay with the feature. The copied core owns rendering and reusable controls, without owning a fetch.
const features = tableFeatures({
sorting: true,
filtering: true,
pagination: true,
})
const table = useTable({ features, columns, data, getRowId })Add only the features the current screen exposes. Formatting belongs in column cells; row actions belong in an explicit actions column.
Keep native table semantics, provide a caption and description, use a stable row identifier, and expose row headers through the starter’s isRowHeader callback. For remote data, choose the appropriate data boundary before enabling server-owned state.