A typed table with sortable columns and loading, empty and error states. Scrolls sideways when narrow.
| Status | |||
|---|---|---|---|
| INV-1044 | Globex | Overdue | $3,120.00 |
| INV-1046 | Umbra Studio | Open | $2,075.25 |
| INV-1042 | Northwind Traders | Paid | $1,280.00 |
| INV-1043 | Acme Supply | Open | $640.50 |
| INV-1045 | Initech | Paid | $92.00 |
pnpm dlx shadcn@latest add @latheui/data-tablePart of Lathe Pro. Set up your project once with your license key: npx latheui init --key <key>. See Lathe Pro.
Data Table is part of Lathe Pro
The preview is live and free to try. The source, the install and every assembly come with Pro: $79 once, with lifetime updates.
Depends on
Also accepts native div attributes for the scroll container.
| Prop | Type | Default | Description |
|---|---|---|---|
| rowSelection | boolean | false | Show row checkboxes and select-visible control. |
| selectedRows / defaultSelectedRows | string[] | — | Controlled or initial selected row ids. |
| onSelectedRowsChange | (ids: string[]) => void | — | Row selection callback. |
| pageSize | number | — | Enables client-side pagination; omitted means all rows. |
| page / defaultPage | number | 1 | Controlled or initial page, starting at one. |
| onPageChange | (page: number) => void | — | Page change callback. |
| columns* (required) | DataTableColumn<T>[] | — | Column definitions. |
| data* (required) | T[] | — | Rows. |
| getRowId* (required) | (row: T) => string | — | Stable id for each row. |
| caption* (required) | string | — | Names the table and its scroll region for assistive tech. |
| sort | SortState | null | — | Controlled sort. Use with onSortChange for server-side sorting. |
| defaultSort | SortState | null | null | Initial sort when uncontrolled. |
| onSortChange | (sort: SortState | null) => void | — | Called when a sortable header is clicked. |
| loading | boolean | false | Replaces the rows with placeholders. |
| loadingRows | number | 5 | Number of placeholder rows. |
| error | ReactNode | — | When set, replaces the rows with this message. |
| onRetry | () => void | — | Adds a “Try again” button to the error state. |
| emptyMessage | ReactNode | "No results." | Shown when there are no rows. |
| Prop | Type | Default | Description |
|---|---|---|---|
| key* (required) | string | — | Unique column id. |
| header* (required) | string | — | Header text. |
| accessor | (row: T) => string | number | — | Value used for sorting and, without cell, for display. |
| cell | (row: T) => ReactNode | — | Custom cell content. |
| sortable | boolean | false | Makes the header a sort button. Needs an accessor. |
| align | "left" | "right" | "left" | Right-align numbers. Right-aligned cells use tabular figures. |
| className | string | — | Classes for the column's header and cells. |
| Prop | Type | Default | Description |
|---|---|---|---|
| key* (required) | string | — | The sorted column's key. |
| direction* (required) | "asc" | "desc" | — | Sort direction. |
Control the sort and fetch with it. Leave out the accessor-based ordering by passing rows that are already sorted.
const [sort, setSort] = React.useState<SortState | null>(null)
const { data, isLoading } = useUsers({ sort })
<DataTable
caption="Users"
columns={columns}
data={data ?? []}
getRowId={(row) => row.id}
sort={sort}
onSortChange={setSort}
loading={isLoading}
/>{ key: "id", header: "ID", accessor: (row) => row.id, className: "w-28 font-mono" }