Skip to content

DataTable

<DataTable> is the high-level list / table primitive. Pass data + columns and it handles header, body, sort, selection, row click, loading skeleton, and empty state. For raw control, use the lower-level Table.

import from "@na/ui/components/DataTable" ▶ Open in Storybook packages/ui/src/components/DataTable.tsx

When to use

  • Every list / table screen with rows of typed data.
  • The default for “I have a list and need to display it”.

Don’t use DataTable for: free-form layouts (Card + CardGrid), trees / hierarchies (use a tree primitive — TBD), or board-style pipelines (BoardView).

Default

Stage
Acme CorpNegotiation$24,000
GlobexDiscovery$8,500
InitechClosed-Won$42,000
SoylentDiscovery$12,000
const columns: DataTableColumn<Row>[] = [
{ id: 'name', header: 'Name', cell: (r) => r.name, sortable: true },
{ id: 'stage', header: 'Stage', cell: (r) => r.stage },
{ id: 'amount', header: 'Amount', cell: (r) => r.amount, numeric: true, sortable: true },
];
<DataTable<Row>
data={rows}
columns={columns}
sortColumn={sortColumn}
sortDirection={sortDirection}
onSortChange={(col, dir) => setSort({ col, dir })}
/>

API

Prop Type Default Description
data * T[] Row data.
columns * DataTableColumn<T>[] Column descriptors — { id, header, cell, sortable?, numeric?, className?, cellClassName? }.
rowId (row: T) => string Stable row id. Defaults to (row as any).id. Required for selection.
sortColumn string | null Controlled active sort column.
sortDirection SortDirection "asc" | "desc" | null. Pair with sortColumn.
onSortChange (column, direction) => void Fires on header click. Caller cycles state.
selectedIds Set<string> Controlled selection. Renders the leading checkbox column when set.
onSelectionChange (ids: Set<string>) => void Fires on row checkbox / select-all toggle.
onRowClick (row: T) => void Whole-row click. Stops propagation in interactive cells.
rowActions (row: T) => ReactNode Trailing actions cell — right-aligned, click stops propagation.
isLoading boolean false Show skeleton rows during initial load.
loadingRowCount number 5 How many skeleton rows.
isFiltered boolean false Picks the right empty-state variant.
emptyState ReactNode Shown when data is empty and !isFiltered.
filteredEmptyState ReactNode Shown when data is empty and isFiltered.

Design guidelines

✓ Do

  • Make every column with sortable data sortable. Users sort more than they expect to.
  • Set isFiltered + filteredEmptyState so "no results" reads correctly.
  • Use rowActions for per-row actions; lift bulk actions to a toolbar above the table.

✗ Don't

  • Render >7 columns by default — beyond that the table becomes a horizontal scroller.
  • Use DataTable for kanban / pipeline data. BoardView is correct for stage-driven flows.
  • Hand-roll sort state. Pass controlled sortColumn + sortDirection.

Accessibility

  • Composes Table under the hood — semantic <table> / <thead> / <tbody> / <tr> / <td>.
  • Sortable headers compose SortableTableHead with aria-sort.
  • Selection uses real <input type="checkbox"> with associated label.

▶ Open DataTable stories in Storybook