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<DataTableFeatures, 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<{ columnFilteringFeature: TableFeature; globalFilteringFeature: TableFeature; rowSortingFeature: TableFeature; rowExpandingFeature: TableFeature; ... 8 more ...; sortFns: { ...; }; }, TData>[]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.
columnPinningPartial<ColumnPinningState>Columns frozen against an edge while the rest pans horizontally, by id: { start: ["name"], end: ["actions"] }. The edges are logical, so they follow the reading direction and swap under RTL. Naming one edge is enough - the other defaults to empty.
columnSizingColumnSizingStateSeeded widths by column id. Only needed to restore a persisted layout.
columnVisibilityColumnVisibilityStateControlled 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<{ columnFilteringFeature: TableFeature; globalFilteringFeature: TableFeature; rowSortingFeature: TableFeature; rowExpandingFeature: TableFeature; ... 8 more ...; sortFns: { ...; }; }, 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: ColumnVisibilityState) => 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.
onSortingChange((state: SortingState) => void)Fires whether or not sorting 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.
renderGalleryCard((row: TData) => ReactNode)Card content for one row in gallery view — compose the CardGallery parts (Logo, Title, Description) or any JSX; the tile itself is supplied and stays clickable through onRowClick.
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.
sortingSortingStateControlled sorting state; omit to let the table own it. When passed, the table stops sorting rows itself (manualSorting) and expects data to arrive already ordered — the server-side sorting mode.
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<{ columnFilteringFeature: TableFeature; globalFilteringFeature: TableFeature; rowSortingFeature: TableFeature; rowExpandingFeature: TableFeature; ... 8 more ...; sortFns: { ...; }; }, TData>) => ReactNode) | ReactNodeEmplacement rendered above the table - compose DataTable.Toolbar with DataTable.Search, DataTable.Filters and DataTable.Actions.
viewDataTableView"table"Row layout. gallery swaps the table for a CardGallery grid built from the same (sorted, filtered) row model. Needs renderGalleryCard; drive it with DataTable.ViewToggle. The pagination footer works in either layout — omit it to show everything at once.

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.