Installation
Theme
Mode

Data display

Table

The dense, sortable grid that backs every list view — tasks, members, invoices. Column sorting, row selection with bulk actions, rich cells, and two densities, dressed in the system's badges, avatars, and checkboxes.

$npx shadcn@latest add @orbit/table

Overview

Click a header to cycle ascending → descending → unsorted; the active column shows a blue arrow and sets aria-sort. Selecting rows tints them, swaps the toolbar for a bulk-action bar with a live count, and the header checkbox toggles all with a tri-state indeterminate when partial.

Live — click headers to sort, checkboxes to select
Search tasks…
6 tasks
Ship onboarding flowORB-1428In progressHighSCSam ChenJun 308
Redesign empty statesORB-1431To doMediumPNPriya NairJul 25
Fix board drag jankORB-1419In reviewUrgentARAlex RiveraJun 263
Automation rules v1ORB-1402In progressHighJLJordan LeeJul 813
Export to CSVORB-1390DoneLowMSMateo SilvaJun 202
Keyboard shortcuts passORB-1377To doMediumDPDana ParkJul 115
Showing 6 of 6

States

The empty slot renders across all columns when rows is empty — hand it an EmptyState. While data loads, render the Skeleton library's table preset in its place so the page doesn't reflow.

Empty — EmptyState in the empty slot
Search tasks…
0 tasks

No tasks yet

Tasks assigned to this project will show up here. Create one to get started.

Loading — SkeletonTable placeholder

Rich cells

Each column renders its own cell — not just text. These reuse the exact Badge, Avatar, and priority treatments from elsewhere in the library, composed here with the low-level Table / TableRow / TableCell parts. Numbers right-align with tabular figures; text and rich cells left-align for a clean rag.

Low-level parts, composed by hand (third row selected)
TaskStatusPriorityAssigneeEst.
Ship onboarding flowORB-1428In progressHighSCSam Chen8
Redesign empty statesORB-1431To doMediumPNPriya Nair5
Fix board drag jankORB-1419In reviewUrgentARAlex Rivera3

Density

Compact tightens rows from 52px to 40px for power users scanning long lists. No toolbar, footer, or selection here — the table also works bare.

density="compact"
Ship onboarding flowORB-1428In progressHighSCSam ChenJun 308
Redesign empty statesORB-1431To doMediumPNPriya NairJul 25
Fix board drag jankORB-1419In reviewUrgentARAlex RiveraJun 263
Automation rules v1ORB-1402In progressHighJLJordan LeeJul 813
Export to CSVORB-1390DoneLowMSMateo SilvaJun 202
Keyboard shortcuts passORB-1377To doMediumDPDana ParkJul 115

Controlled

Sort and selection both work controlled or uncontrolled. Pass sort / onSortChange and selectedIds / onSelectionChange to lift the state; leave them off and the table manages itself (seed with defaultSort / defaultSelectedIds).

Export to CSVORB-1390DoneLowMSMateo SilvaJun 202
Fix board drag jankORB-1419In reviewUrgentARAlex RiveraJun 263
Ship onboarding flowORB-1428In progressHighSCSam ChenJun 308
Redesign empty statesORB-1431To doMediumPNPriya NairJul 25
Automation rules v1ORB-1402In progressHighJLJordan LeeJul 813
Keyboard shortcuts passORB-1377To doMediumDPDana ParkJul 115

sort: due asc · selected: [ORB-1428]

Props

PropTypeDefaultDescription
columnsDataTableColumn<T>[]Column definitions — key, header, sortable, align, width, cell renderer, sortValue.
rowsT[]Row data, one object per row.
getRowId(row: T) => stringrow.idStable row identity used for selection and React keys.
selectablebooleanfalseLeading 40px checkbox column with a tri-state select-all header.
selectedIdsstring[]Controlled selection. Pair with onSelectionChange.
defaultSelectedIdsstring[][]Uncontrolled initial selection.
onSelectionChange(ids: string[]) => voidFires with the full next selection.
sortDataTableSort | nullControlled sort ({ key, dir }; null = natural order). Pair with onSortChange.
defaultSortDataTableSort | nullnullUncontrolled initial sort.
onSortChange(sort: DataTableSort | null) => voidFires as headers cycle asc → desc → unsorted.
density"default" | "compact""default"Row height — 52px or 40px. The 38px header is constant.
stickyHeaderbooleanfalsePins header cells to the top of the scroll container.
toolbarReactNodeIdle toolbar content (left side). The toolbar renders when this or bulkActions is set.
metaReactNode"N rows"Right side of the toolbar — the row count readout.
bulkActionsReactNode | (ids) => ReactNodeShown next to the “N selected” count while rows are selected.
footerReactNodeRight side of the footer bar (e.g. pagination). The left side is an automatic selection summary.
rowActions(row: T) => ReactNodeTrailing 44px actions column, rendered per row.
emptyReactNodeRendered across all columns when rows is empty.
containerClassNamestringClass for the scrollable table wrapper — e.g. a max-height for sticky headers.
…restHTMLAttributes<div>Forwarded to the rounded card root.

DataTableColumn props

Everything a column needs in one object. When cell or sortValue are omitted, the column falls back to reading row[key] directly.

PropTypeDefaultDescription
keystringUnique column id; doubles as the row field for the default cell and sort value.
headerReactNodeHeader label — rendered inside a real <button> when sortable.
sortablebooleanfalseEnables the three-state sort cycle and aria-sort on the header.
align"left" | "right""left"Right-aligned columns get 14px edge padding and a row-reversed header.
widthnumber | stringFixed column width.
cell(row: T) => ReactNodeCell renderer — badges, avatars, glyphs, anything.
sortValue(row: T) => string | numberComparable value for sorting (dates as timestamps, priorities as levels…).
headClassName / cellClassNamestringExtra classes on the <th> / each <td>.

Usage

For layouts DataTable doesn't cover, compose the styled primitives directly: Table (density, containerClassName), TableHeader, TableBody, TableRow (selected), TableHead, TableCell, TableFooter, and TableCaption. They carry the spec's metrics — 38px mono-caps header, 52/40px rows, surface-2 dividers, hover and selected tints — and everything else is plain table markup.

Do

Right-align numbers

Tabular, right-aligned figures make columns of values easy to compare at a glance.

Don’t

Pack in every field

Show the columns people scan; move the rest into a row-detail or expand.

Do

Surface bulk actions on select

When rows are selected, replace the toolbar with the actions that apply to them.

Don’t

Sort multiple columns at once

Multi-sort is hard to read; keep one active sort with a clear direction arrow.

Keyboard & accessibility

Semantic <table> / <thead> / <tbody> markup with scope=col on header cells. Sortable headers are real buttons — Tab reaches them, Enter and Space cycle the sort, and the sorted column exposes aria-sort. Checkboxes are the registry Checkbox: Space toggles a row, and the labelled header checkbox toggles all with an indeterminate middle state.

Tab through the header buttons and checkboxes
Ship onboarding flowORB-1428In progressHighSCSam ChenJun 308
Redesign empty statesORB-1431To doMediumPNPriya NairJul 25
Fix board drag jankORB-1419In reviewUrgentARAlex RiveraJun 263
Automation rules v1ORB-1402In progressHighJLJordan LeeJul 813