Reference | Client | Role | Status | Amount |
|---|---|---|---|---|
PRJ-001 | Riverside Studio | Designer | Confirmed | $180 |
PRJ-002 | Northgate Labs | Developer | Pending | $240 |
PRJ-003 | Harbour Media | Copywriter | Confirmed | $150 |
PRJ-004 | Eastfield Group | Designer | Completed | $210 |
PRJ-005 | Southbank Digital | Consultant | Pending | $320 |
const columns: ColumnDef<Project>[] = [
{ accessorKey: "reference", header: "Reference", size: 130 },
{ accessorKey: "client", header: "Client" },
{ accessorKey: "amount", header: "Amount", size: 110 },
];
<DataTable.Root columns={columns} data={projects} />;A column with a numeric size switches the table to fixed layout, and sized
cells truncate instead of stretching.
API
DataTable.Root
| Prop | Type | Default | |
|---|---|---|---|
columns | ColumnDef<TData, TValue>[] | required | TanStack column definitions. A numeric size on any column switches the
table to fixed layout, and sized cells truncate instead of stretching. |
data | readonly TData[] | required | The rows to render, already fetched and in the order you want them. The table sorts and filters its own view of this array but never mutates it. |
columnPinning | ColumnPinningState | — | Columns frozen against an edge while the rest pans horizontally, by id:
{ left: ["name"], right: ["actions"] }. |
columnSizing | ColumnSizingState | — | Seeded widths by column id. Only needed to restore a persisted layout. |
columnVisibility | VisibilityState | — | Controlled column visibility; pair with DataTable.ViewOptions. |
containerClassName | string | — | Forwarded to the underlying Table scroll container. |
density | DataTableDensity | "comfortable" | Row height. compact fits about a third more rows on a screen. |
emptyState | ReactNode | — | Custom empty state; defaults to a generic "No results" block. |
enableColumnResizing | boolean | false | Lets the user drag column edges. Widths live in the table's own state, so
onColumnSizingChange is only needed to persist them across mounts. |
enableRowSelection | boolean | ((row: Row<TData>) => boolean) | false | Per-row or table-wide opt-in, forwarded to |
getRowId | ((row: TData, index: number) => string) | — | Stable row ids for selection across pages; defaults to the row index. |
globalFilter | string | — | Client-side search across every visible cell. Leave undefined for
server-side search (drive DataTable.Search yourself instead). |
initialSorting | SortingState | — | Sorting is managed internally; this seeds the initial column order. |
loading | boolean | false | Refetch in progress: keeps the current rows visible under a spinner. |
onColumnPinningChange | ((state: ColumnPinningState) => void) | — | Fires whether or not columnPinning is passed. |
onColumnSizingChange | ((state: ColumnSizingState) => void) | — | Fires on every drag frame; debounce before writing it to storage. |
onColumnVisibilityChange | ((state: VisibilityState) => void) | — | Fires whether or not columnVisibility is passed. Persist it to remember a user's columns. |
onRowClick | ((row: TData) => void) | — | When set, rows become clickable and invoke this with the row's data. |
onRowSelectionChange | ((state: RowSelectionState) => void) | — | Fires whether or not rowSelection is passed, so it works as a listener too. |
pagination | Props | — | Server-side pagination footer rendered below the table. |
renderExpandedRow | ((row: TData) => ReactNode) | — | Detail panel for an expanded row, rendered as a full-width row beneath it. Rows become expandable and grow a caret in the first cell. |
renderMobileCard | ((row: TData) => ReactNode) | — | Card content for one row on mobile. When provided, the table is replaced
below the md breakpoint by a vertical list of cards (no horizontal
scroll) built from the sorted row model; cards stay clickable through
onRowClick. The toolbar and pagination footer remain visible. |
rowSelection | RowSelectionState | — | Controlled selection state; omit to let the table own it. |
stickyHeader | boolean | false | Keeps the header row visible while the body scrolls. Needs a bounded
height on the scroll container - pass e.g. max-h-96 through
containerClassName. |
toolbar | ((table: Table<TData>) => ReactNode) | ReactNode | — | Emplacement rendered above the table - compose DataTable.Toolbar with
DataTable.Search, DataTable.Filters and DataTable.Actions. |
Plus every <div> prop. Source: data-table/components/data-table-root.tsx.
Every extra is opt-in and every piece of state is optionally controlled: pass
nothing and the table owns it, pass the value and you do. The on*Change
callbacks fire either way, so they double as listeners when you only want to
persist a user's layout.
DataTable.Toolbar
| Prop | Type | Default | |
|---|---|---|---|
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <div> prop. Source: data-table/components/data-table-toolbar.tsx.
The strip above the table. It is a layout slot, not a behaviour —
Search, DataTable.Filters and
DataTable.Actions go inside it. Pass toolbar a function instead of a node
when a control inside needs the table instance;
the column menu and the exporter both do.
DataTable.Actions
| Prop | Type | Default | |
|---|---|---|---|
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <div> prop. Source: data-table/components/data-table-actions.tsx.
The right-hand end of the toolbar, where the controls that act on the whole table live.
DataTable.Filters
| Prop | Type | Default | |
|---|---|---|---|
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <div> prop. Source: data-table/components/data-table-filters.tsx.
The left-hand end, for the filter controls themselves. For a full filtering surface rather than a few selects, ui-filter composes with this.
DataTable.Empty
| Prop | Type | Default | |
|---|---|---|---|
bordered | boolean | — | Draws a dashed frame around the block, for an empty state standing on its own. Leave it off inside something that already has a border — a card, or a table body. |
description | string | "Try adjusting your search or filters." | One line under the title — the place to suggest clearing a filter. |
title | string | "No results" | Headline of the block. Defaults to a generic "No results". |
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus the DOM props of the element it renders. Source: data-table/components/data-table-empty.tsx.
What renders in place of rows when nothing matched. DataTable.Root shows a
generic one already; reach for this when you want to name the filter to clear,
and pass it through Root's emptyState.