Data Table
Table, filter, column picker, pagination and row selection in one piece. Sorting, filtering and paging run in plain React over the rows you pass in.
| INV-1042 | Acme Corpbilling@acmecorp.com | paid | $250.00 | |
| INV-1043 | Globexbilling@globex.com | pending | $981.00 | |
| INV-1044 | Initechbilling@initech.com | overdue | $1,712.00 | |
| INV-1045 | Umbrellabilling@umbrella.com | paid | $2,443.00 | |
| INV-1046 | Hoolibilling@hooli.com | pending | $3,174.00 | |
| INV-1047 | Vehement Capitalbilling@vehementcapital.com | overdue | $3,905.00 |
0 of 23 selected
Page 1 of 4
Installation
pnpm dlx nachui add data-tableAnatomy
With no children the table lays itself out: the filter and the column picker on top, the table in the middle, the selection count and the pager at the bottom. Everything runs on the array you pass in, so there is no fetching, no adapter and no library underneath.
Pass children to keep only the parts you want. Every part reads the same context, so a table alone still filters, sorts and pages through whatever you wire to
useDataTable.Composition
A hybrid is visible composition. Open
data-table.tsx and these are the elements it is made of, in this order:Column shape
A column says how to read the row and how to show it.
cell renders, sortValue and filterValue are what sorting and filtering compare. When a column has no filterValue the table falls back to sortValue, then to the cell if it is plain text.| Field | Type | Description |
|---|---|---|
id | string | Stable key, also the name in sort |
header | ReactNode | Header cell, used as the label in the column menu |
cell | (row: T) => ReactNode | Body cell |
sortable | boolean | Turns the header into a sort button |
sortValue | (row: T) => string | number | Date | Value compared when sorting |
filterValue | (row: T) => string | Text searched by the filter |
align | 'start' | 'end' | end right-aligns and sets tabular numerals |
hidden | boolean | Hidden at first, still available in the menu |
width | string | Any CSS width for the header cell |
Sorting
Click a sortable header to sort ascending, again for descending, a third time to clear. The sort is stable, so rows that tie keep the order they came in. The header carries
aria-sort for assistive tech.Selection
Set
selectable for a checkbox column. The header box selects the current page, and turns indeterminate when only some of it is selected. Selection is a set of row ids from getRowId, so it survives sorting, filtering and paging. Read it from onSelectedChange or control it with selected.Controlled state
Filter, sort and selection are uncontrolled by default. Pass
filter, sort or selected with their change handlers to own them, for a URL, a store or a server query. The pure applyDataTable helper is what the component runs, so you can run the same logic elsewhere.Compact
Keep only the table when the toolbar and footer would be noise.
| Version | Date | Changes |
|---|---|---|
| 1.4.0 | 2026-09-18 | 12 |
| 1.3.2 | 2026-09-02 | 3 |
| 1.3.1 | 2026-08-27 | 1 |
| 1.3.0 | 2026-08-20 | 9 |
| 1.2.0 | 2026-07-30 | 15 |
API Reference
DataTable
| Prop | Type | Default | Description |
|---|---|---|---|
data | T[] | - | Every row, before filtering and paging |
columns | DataTableColumn<T>[] | - | Column definitions |
getRowId | (row: T) => string | - | Stable id per row, used for keys and selection |
pageSize | number | 10 | Rows per page |
selectable | boolean | false | Adds the checkbox column |
selected | Set<string> | string[] | - | Controlled selection |
onSelectedChange | (selected: string[]) => void | - | Called when selection changes |
sort | { id, direction } | null | - | Controlled sort |
onSortChange | (sort) => void | - | Called when sort changes |
filter | string | - | Controlled filter text |
onFilterChange | (filter: string) => void | - | Called when the filter changes |
filterPlaceholder | string | 'Filter…' | Placeholder of the filter input |
emptyTitle | string | 'No results' | Title of the empty state |
emptyDescription | string | - | Description of the empty state |
labels | Partial<DataTableLabels> | - | Every visible string, with English defaults |
children | ReactNode | - | Custom layout, replaces the default parts |
className | string | - | Additional CSS classes |
DataTable.Toolbar
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Extra actions next to the filter |
className | string | - | Additional CSS classes |
DataTable.Content
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
DataTable.Footer
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Extra content next to the selection count |
className | string | - | Additional CSS classes |
Helpers
| Signature | Description |
|---|---|
applyDataTable(data, { columns, filter, sort, page, pageSize }) | Returns { rows, total, pageCount }, the same logic the table runs |
useDataTable() | The table context, for custom parts inside DataTable |
Found something to improve?
Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.