Components
DataTable
Columns + rows with sorting, row selection, row click, skeleton loading, an empty state, responsive columns and a sticky header. Built on the Table primitives.
Usage
Use it for:
- Lists of records people scan, sort and act on (transactions, tasks, invoices).
- Bulk actions on a selection.
Avoid it when:
- Fewer than ~5 rows of reference data: a plain Table or a list reads better.
- Cells need inline editing like a spreadsheet: use the database TableView module.
- Cards with images or rich previews: use a Grid of Cards.
Installation
Install the package (React 19):
terminal
terminal
terminal
Import the styles once, in your app entry or root layout:
/src/main.tsx
Examples
Transactions: sorting, selection and pagination
Controlled sort and selection; the page slices the sorted rows and Pagination moves through them. Category and Account hide on small screens (hideBelow).
12 transactions
| Account | |||||
|---|---|---|---|---|---|
| Oct 18 | Northwind Coffee | Food | Checking | -$4.80 | |
| Oct 17 | Acme Payroll | Income | Checking | $4,250.00 | |
| Oct 16 | Metro Transit | Transport | Card •• 4411 | -$32.00 | |
| Oct 15 | Greenleaf Market | Food | Card •• 4411 | -$86.45 | |
| Oct 14 | Cloudhost | Software | Card •• 4411 | -$24.00 |
Uncontrolled sorting, row click and sticky header
Without sort the table sorts itself (numbers numerically, text naturally, dates in order). maxHeight keeps the header in view. onRowClick makes the merchant cell a button.
| Account | ||||
|---|---|---|---|---|
| City Apartments | Housing | Checking | -$1,450.00 | |
| Greenleaf Market | Food | Card •• 4411 | -$86.45 | |
| Power & Light | Utilities | Checking | -$64.20 | |
| Fitness Club | Health | Card •• 4411 | -$45.00 | |
| Metro Transit | Transport | Card •• 4411 | -$32.00 | |
| Metro Transit | Transport | Card •• 4411 | -$32.00 | |
| Cloudhost | Software | Card •• 4411 | -$24.00 | |
| Bright Books | Shopping | Card •• 4411 | -$18.99 | |
| Northwind Coffee | Food | Card •• 4411 | -$5.10 | |
| Northwind Coffee | Food | Checking | -$4.80 | |
| Freelance invoice #208 | Income | Savings | $900.00 | |
| Acme Payroll | Income | Checking | $4,250.00 |
Built-in paging
pageSize pages the sorted rows and adds a Pagination footer; pass page / onPageChange to control it. Sorting returns to page 1.
| Account | ||||
|---|---|---|---|---|
| Oct 17 | Acme Payroll | Income | Checking | $4,250.00 |
| Oct 9 | Freelance invoice #208 | Income | Savings | $900.00 |
| Oct 18 | Northwind Coffee | Food | Checking | -$4.80 |
| Oct 6 | Northwind Coffee | Food | Card •• 4411 | -$5.10 |
Loading and empty
| Account |
|---|
API Reference
DataTable
Generic over the row type: DataTable<Row>.
| Prop | Type | Default |
|---|---|---|
columns | DataTableColumn<Row>[] | |
rows | Row[] | |
getRowId | (row: Row) => string | |
sort | { key: string; direction: "asc" | "desc" } | null | |
defaultSort | DataTableSort | null | null |
onSortChange | (sort: DataTableSort | null) => void | |
selectedIds | string[] | |
defaultSelectedIds | string[] | [] |
onSelectedIdsChange | (ids: string[]) => void | |
selectable | boolean | false |
onRowClick | (row: Row) => void | |
loading | boolean | false |
loadingRows | number | 5 |
empty | ReactNode | "No results" |
density | "compact""comfortable" | "compact" |
maxHeight | number | string | |
label | string | |
getRowLabel | (row: Row) => string | |
pageSize | number | |
page | number | |
defaultPage | number | 1 |
onPageChange | (page: number) => void | |
ref | Ref<HTMLTableElement> | |
style | CSSProperties | |
id | string | |
className | string |
DataTableColumn
One column definition.
| Prop | Type | Default |
|---|---|---|
key | string | |
header | ReactNode | |
cell | (row: Row) => ReactNode | |
sortValue | (row: Row) => string | number | boolean | Date | null | |
align | "start""center""end" | "start" |
sortable | boolean | false |
width | number | string | |
hideBelow | "sm""md""lg" |
Accessibility
Keyboard:
- Tab: header checkbox → sort buttons → each row’s checkbox and row button, in reading order.
- Space: toggles a checkbox. Enter / Space on a sort button cycles the sort; on a row button, opens the row.
- ← →: scroll the table container when it is wider than the screen.
ARIA and semantics:
- Native table semantics with
aria-sorton sortable headers. - The select-all checkbox is mixed (indeterminate) when some rows on the page are selected.
- Row checkboxes are named "Select <first column text>" (override with
getRowLabel). - Row click never nests controls: the keyboard target is one button in the first cell, and clicks on other controls in the row are ignored.
- While loading, the table is
aria-busyand a polite status says "Loading"; skeletons are hidden from assistive technology.
© 2026 Motion. Internal documentation.