DataTable

Signal-driven data table with client/server sort, filter, pagination, multi-selection, expandable rows, and CSS content-visibility row containment for large datasets

Category: display · WCAG 2.2-AA · table, data, grid, sort, filter, pagination, selection

Sizes

States

Props

PropTypeDefaultDescription
virtualizedbooleanfalseRender only the visible row window for large datasets.
rowHeightnumber40Fixed row height in px, used to compute the virtualized window.
windowSizenumber20Number of rows rendered in the virtualized window.
overscannumber3Extra rows rendered above/below the window to smooth scrolling.
columnsColumn<Row>[]The column definitions describing each table column.
rowsRow[]The row objects to render — one table row per array element.
getRowId(row: Row) => stringReturns a stable unique id for a row.
sortSortStateThe controlled sort state.
defaultSortSortStateThe initial sort state when uncontrolled.
sortMode'client' | 'server''client'Whether sorting is handled client-side or by the server ('client' | 'server').
onSortChange(sort: SortState | undefined) => voidCalled with the new sort state when it changes.
searchablebooleanfalseWhen true, shows a search/filter input.
pagination{ pageSize: number; pageSizeOptions?: number[] }Pagination configuration (page size and options).
selection{ mode: 'single' | 'multi'; selected?: string[]; onChange?: (ids: string[]) => void }Row-selection configuration (mode and selected ids).
batchActions{ label: string; onClick: (selectedIds: string[]) => void }[]Actions applied to the currently selected rows.
renderExpandedRow(row: Row) => ReactNodeRenders the expanded content for a row.
density'compact' | 'normal' | 'relaxed''normal'Row density — 'compact', 'normal', or 'relaxed'.
zebrabooleanfalseWhen true, applies alternating row striping.
stickyHeaderbooleanfalseWhen true, the header stays fixed while the body scrolls.
loadingbooleanfalseWhen true, shows a loading state.
emptyStateReactNodeContent shown when there are no rows.
titlestringTitle text for the component.
descriptionstringSupporting description text.
labelsDataTableLabelsOverrides for the component’s user-visible strings (i18n).
classNamestringAdditional CSS class names merged onto the root element.

Design tokens

When to use

When not to use

How to build an accessible DataTable in React →

Examples

Basic table

<DataTable
  columns={[
    { key: 'name', header: 'Name', sortable: true },
    { key: 'role', header: 'Role' },
  ]}
  rows={[
    { name: 'Alice', role: 'Engineer' },
    { name: 'Bob', role: 'Designer' },
  ]}
  getRowId={(r) => r.name}
/>

Custom cell content with Column.render

Use Column.render to return any ReactNode per cell — a Badge for status, an icon + link for a repo, a right-aligned timestamp. Columns without render fall back to String(row[key]).

<DataTable
  columns={[
    { key: 'name', header: 'Project', sortable: true },
    {
      key: 'status',
      header: 'Status',
      render: (row) => (
        <Badge variant={row.status === 'ready' ? 'success' : 'warning'}>{row.status}</Badge>
      ),
    },
    {
      key: 'updated',
      header: 'Updated',
      align: 'end',
      render: (row) => new Date(row.updated).toLocaleDateString(),
    },
  ]}
  rows={rows}
  getRowId={(r) => r.name}
/>

Full-featured: selection, pagination, search

<DataTable
  columns={columns}
  rows={rows}
  getRowId={(r) => r.id}
  searchable
  pagination={{ pageSize: 10, pageSizeOptions: [10, 25, 50] }}
  selection={{ mode: 'multi', onChange: setSelected }}
  batchActions={[{ label: 'Delete', onClick: deleteRows }]}
  stickyHeader
  zebra
/>

Related components

← Back to docs