Data Table
A table with sortable columns and a text filter for client-side data.
- Static data with no sorting: use Table.
- Thousands of rows: sort and filter on the server and pass the page of rows.
Install
Terminal
pnpm dlx shadcn@latest add @acme/data-tableOr import from the package: import { DataTable } from '@acme/react'
Examples
Table basic
5 of 5 rows
| INV-1042 | Northwind Traders | Paid | $1,250.00 |
|---|---|---|---|
| INV-1043 | Contoso Ltd | Overdue | $860.00 |
| INV-1044 | Fabrikam | Draft | $4,300.00 |
| INV-1045 | Adventure Works | Paid | $2,175.00 |
| INV-1046 | Tailspin Toys | Overdue | $540.00 |
data-table-basic.tsx
'use client'
import { Badge } from '@/components/ui/badge'
import { DataTable, type DataTableColumn } from '@/components/ui/data-table'
type Invoice = {
id: string
customer: string
status: 'Paid' | 'Overdue' | 'Draft'
amount: number
}
const invoices: Invoice[] = [
{ id: 'INV-1042', customer: 'Northwind Traders', status: 'Paid', amount: 1250 },
{ id: 'INV-1043', customer: 'Contoso Ltd', status: 'Overdue', amount: 860 },
{ id: 'INV-1044', customer: 'Fabrikam', status: 'Draft', amount: 4300 },
{ id: 'INV-1045', customer: 'Adventure Works', status: 'Paid', amount: 2175 },
{ id: 'INV-1046', customer: 'Tailspin Toys', status: 'Overdue', amount: 540 },
]
const STATUS = { Paid: 'success', Overdue: 'destructive', Draft: 'secondary' } as const
const usd = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })
const columns: DataTableColumn<Invoice>[] = [
{ key: 'id', header: 'Invoice', sortable: true },
{ key: 'customer', header: 'Customer', sortable: true },
{
key: 'status',
header: 'Status',
sortable: true,
cell: (r) => <Badge variant={STATUS[r.status]}>{r.status}</Badge>,
},
{
key: 'amount',
header: 'Amount',
sortable: true,
align: 'end',
cell: (r) => usd.format(r.amount),
},
]
export default function DataTableBasic() {
return (
<DataTable
caption="Invoices"
columns={columns}
data={invoices}
rowKey="id"
filterKeys={['id', 'customer', 'status']}
/>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| caption (required) | string | — | Names the table and its filter. |
| columns (required) | DataTableColumn<T>[] | — | key, header, cell, sortable, align. |
| data (required) | T[] | — | Rows. |
| rowKey (required) | keyof T | — | Unique row key. |
| rowHeader | keyof T | first column | Column used as row headers. |
| filterKeys | (keyof T)[] | — | Columns the text filter searches. |
| emptyMessage | string | 'No matching rows.' | Shown when nothing matches. |
Accessibility
| Key | Action |
|---|---|
| Tab | Moves to sort buttons and the filter |
| Enter / Space | Toggles sort: ascending, descending, none |
- Role
- table with sortable column headers
- You provide
- A caption.
- Built in
- Sort state uses aria-sort on the header cell.
- The row count and sort order are announced in a status region after each change.
- WCAG 2.2 criteria
- 1.3.1, 2.1.1, 4.1.3
Don’t
- Sort columns that have no meaningful order.
Tokens
- color.border
- color.muted-foreground
- color.foreground