DataTable

The table itself: sortable columns, a mobile card fallback, and every extra behind a prop.

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

PropTypeDefault
columnsColumnDef<TData, TValue>[]requiredTanStack column definitions. A numeric size on any column switches the table to fixed layout, and sized cells truncate instead of stretching.
datareadonly TData[]requiredThe 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.
columnPinningColumnPinningStateColumns frozen against an edge while the rest pans horizontally, by id: { left: ["name"], right: ["actions"] }.
columnSizingColumnSizingStateSeeded widths by column id. Only needed to restore a persisted layout.
columnVisibilityVisibilityStateControlled column visibility; pair with DataTable.ViewOptions.
containerClassNamestringForwarded to the underlying Table scroll container.
densityDataTableDensity"comfortable"Row height. compact fits about a third more rows on a screen.
emptyStateReactNodeCustom empty state; defaults to a generic "No results" block.
enableColumnResizingbooleanfalseLets the user drag column edges. Widths live in the table's own state, so onColumnSizingChange is only needed to persist them across mounts.
enableRowSelectionboolean | ((row: Row<TData>) => boolean)falsePer-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.
globalFilterstringClient-side search across every visible cell. Leave undefined for server-side search (drive DataTable.Search yourself instead).
initialSortingSortingStateSorting is managed internally; this seeds the initial column order.
loadingbooleanfalseRefetch 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.
paginationPropsServer-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.
rowSelectionRowSelectionStateControlled selection state; omit to let the table own it.
stickyHeaderbooleanfalseKeeps 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) | ReactNodeEmplacement 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

PropTypeDefault
renderComponentRenderFn<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

PropTypeDefault
renderComponentRenderFn<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

PropTypeDefault
renderComponentRenderFn<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

PropTypeDefault
borderedbooleanDraws 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.
descriptionstring"Try adjusting your search or filters."One line under the title — the place to suggest clearing a filter.
titlestring"No results"Headline of the block. Defaults to a generic "No results".
renderComponentRenderFn<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.