Skip to main content

Design system components

DataTable

Accessible data table with typed columns, three-state sortable headings, optional row selection and pagination, and density variants. Composes the Table primitives and the useTable* hooks.

Status: beta. Group: data-display. Markdown version: /design-system/components/data-table.md

When to use

Use DataTable for structured records that users need to compare, sort, or select. Provide stable row identifiers and a meaningful caption.

When not to use

  • Do not use it for page layout.
  • Do not omit a meaningful caption.
  • Do not use unstable row indexes as identifiers.
  • Do not use DataTable as a page-layout grid or a visual card collection.

Look-alikes

The contract names no look-alike with a deciding condition.

Props

DataTable props
PropTypeDefaultDescriptionRequired
dataRow[]noneTable rows.Yes
columnsDataTableColumn<Row>[]noneOrdered column definitions.Yes
getRowId(row: Row) => stringnoneStable row identifier.Yes
captionstringnoneAccessible table name rendered as a caption.Yes
hideCaptionbooleanfalseVisually hides the caption while retaining the accessible name.No
sortDataTableSort | nullnoneControlled sort state.No
defaultSortDataTableSort | nullnullInitial uncontrolled sort state.No
onSortChange(sort: DataTableSort | null) => voidnoneReceives sort changes.No
selectedRowIdsstring[]noneControlled selected row identifiers; enables selection.No
defaultSelectedRowIdsstring[]noneInitial uncontrolled selection; also enables selection.No
onSelectionChange(rowIds: string[]) => voidnoneReceives selected row identifiers; also enables selection.No
getRowLabel(row: Row) => stringnoneAccessible row label used by selection checkboxes.No
pageSizenumbernoneRows per page; enables pagination when set.No
emptyStateReact.ReactNodeNo dataEmpty-state cell content.No
stripedbooleanfalseAlternating row surfaces.No
stickyHeaderbooleanfalsePin the header row while the body scrolls.No
size"sm" | "md" | "lg"mdDensity scale.No
classNamestringnoneNo description in the contract.No

Variants

  • Available sizes: sm, md, lg (default md).
  • Allowed striped values: true, false (default false).

Invalid combinations

  • MUST NOT: sort and defaultSort set together. sort makes the component controlled and defaultSort uncontrolled; pass one, not both.
  • MUST NOT: selectedRowIds and defaultSelectedRowIds set together. selectedRowIds makes the component controlled and defaultSelectedRowIds uncontrolled; pass one, not both.

Keyboard

DataTable keyboard behaviour
KeyBehaviour
TabMoves focus to the control and on to the next focusable element. (standard)
SpaceActivates or toggles the focused control. (standard)
EnterActivates the focused control. (standard)

Rows marked (standard) name a key the contract lists without a description; the behaviour shown is the usual one for that key.

Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

DataTable rules
LevelRuleHow it is checked
MUST NOTsort and defaultSort set together. sort makes the component controlled and defaultSort uncontrolled; pass one, not both.Checked by the MCP tool validate_component_usage.
MUST NOTselectedRowIds and defaultSelectedRowIds set together. selectedRowIds makes the component controlled and defaultSelectedRowIds uncontrolled; pass one, not both.Checked by the MCP tool validate_component_usage.
MUSTARIA: Native table, thead, tbody, th and td semantics.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Caption supplies the accessible table name.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Sortable headings expose aria-sort.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Selection uses labelled native checkboxes.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTRequired stories produce no axe violations at the configured severity.Automated by npm run test:stories:matrix:ci. WCAG 2.5.8.
MUSTThe accessibility tree (role, name, state) matches the committed snapshot for every required story.Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2.
MUSTThe component remains legible and operable under a real browser forced-colors: active pass.Automated by npm run test:stories:hc.
MUSTEvery documented keyboard interaction works as specified.Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2.
MUSTA human completed an end-to-end accessibility review of this component.Manual review, last on 2026-07-26.
SHOULDGive every table a concise caption that explains what its rows represent.Not stated in the contract.
SHOULDUse stable domain identifiers so selection survives sorting and data refreshes.Not stated in the contract.
SHOULDMake a column sortable only when its values have an understandable order.Not stated in the contract.
SHOULDKeep cell content concise and move detailed content into a dedicated view.Not stated in the contract.
SHOULD NOTDo not hide row actions until pointer hover; keyboard users must find them too.Not stated in the contract.

Install and import

  • Install: npm install @digitaltableteur/react (version 0.9.1, restricted access on npm).
  • Import: import { DataTable } from "@digitaltableteur/react";
  • Entry point: @digitaltableteur/react/content exports it too.
  • Styles: import @digitaltableteur/react/style.css once at the app boundary.