Table

Shows rows of readings, devices or records, with sorting, selection and three densities.

Live React portReact component: TableRoot, Table, TableBody, TableRow, TableHead, TableCell, TableCheck

Try it

Change the density and the ground, then click a header to sort.

Last ReadingTarget %
Bore 122m ago34.150.0−15.9
Bore 74m ago48.950.0−1.1
Bore 33m ago61.250.0+11.2
Ridge Tank11m ago72.460.0+12.4
South Pivot1h ago28.645.0−16.4
North PaddockMar 14, 2026No value45.0No value
Density
Ground
Selectable
Tone
<TableRoot density="default">
  <Table minWidth={520}>
    <TableHeader>…</TableHeader>
    <TableBody>
      <TableRow>
        <TableCell>Bore 12</TableCell>
        <TableCell numeric tone="bad">−15.9</TableCell>
      </TableRow>
    </TableBody>
  </Table>
</TableRoot>
Density changes the row height only. The header stays at 40px.

When to use

  • Use a table to compare readings, devices or records across the same columns.
  • Use it when people sort, scan or select rows.
  • Pair it with Pagination when the rows run past one screen.
Don’t use it
  • For a few values about one site. Use a Card.
  • For a reading over time. Use a Line chart.

Anatomy

Set striped, bordered and interactive once on TableBody, so every row matches.

SiteLast ReadingSoil %Target %Gap %
Bore 122m ago34.150.0−15.9
Bore 74m ago48.950.0−1.1
Bore 33m ago61.250.0+11.2
Ridge Tank11m ago72.460.0+12.4
South Pivot1h ago28.645.0−16.4
North PaddockMar 14, 2026No value45.0No value
5 of 6 sites reporting
<TableRoot density="default">
  <Table>
    <TableColgroup><TableCol width="15%" /></TableColgroup>
    <TableHeader>
      <TableRow><TableHead numeric>Soil %</TableHead></TableRow>
    </TableHeader>
    <TableBody striped interactive>…</TableBody>
    <TableFooter>…</TableFooter>
  </Table>
</TableRoot>

Sorting and selection

Click a header, then tick some rows.

Last ReadingTarget %
Bore 122m ago34.150.0−15.9
Bore 74m ago48.950.0−1.1
Bore 33m ago61.250.0+11.2
Ridge Tank11m ago72.460.0+12.4
South Pivot1h ago28.645.0−16.4
North PaddockMar 14, 2026No value45.0No value

No rows selected · 6 shown

Sorting cycles ascending, descending, then off. Empty values stay at the bottom.

Tone

Tone a cell when the number is the finding, such as the gap to target.

SiteLast ReadingSoil %Target %Gap %
Bore 122m ago34.150.0−15.9
Bore 74m ago48.950.0−1.1
Bore 33m ago61.250.0+11.2
Ridge Tank11m ago72.460.0+12.4
South Pivot1h ago28.645.0−16.4
North PaddockMar 14, 2026No value45.0No value

Empty and unknown values

Pass an empty state to empty on TableRoot. The header stays.

SiteLast ReadingSoil %Target %Gap %
No sites match this filterSix sites are hidden by the current filter.
A cell with no value shows an em dash, and a screen reader hears “No value”.

Content

  • Write headers as short nouns in Title Case: Site, Last Reading.
  • Put the unit in the header, so cells hold only the figure: Soil %.
  • Give every value in a column the same decimal places.

Accessibility

A sortable header announces the order it will switch to. Name each row’s checkbox after the row, and announce the selected count.

KeyWhat it does
TabMoves through sort headers, select-all and row checkboxes.
Enter / SpaceChanges the sort, or ticks a row’s checkbox.

Props

PropTypeDefaultDescription
TableRoot density'compact' | 'default' | 'comfortable''default'Row height: 32, 40 or 48px.
TableRoot maxHeightnumber—Scrolls rows under a sticky header.
TableRoot emptyReactNode—Shown under the header.
Table selectablebooleanfalseSets aria-selected on rows. TableCheck announces the state.
Table minWidthnumber—Scrolls sideways below this width.
TableCol widthnumber | string—Column width, in px or percent.
TableBody stripedbooleanfalseShades every second row.
TableBody borderedbooleanfalseRules between rows and cells.
TableBody interactivebooleanfalseHover ground for clickable rows.
TableRow selectedbooleanfalseBlue tint and a 3px start bar.
TableRow onClick() => void—Pointer shortcut. Pair with TableCheck.
TableHead sort'asc' | 'desc' | 'none'—Sets the caret and aria-sort.
TableHead onSort() => void—Makes the whole header cell a sort button.
TableHead labelstring—Sort button name when children aren’t text.
TableHead numericbooleanfalseRight-aligns the label.
TableCell numericbooleanfalseRight-aligns, with tabular figures.
TableCell tone'ok' | 'note' | 'act' | 'bad' | 'live' | 'idle'—Colours the value as a finding.
TableCell mutedbooleanfalseSecondary ink.
TableCheck checkedboolean—The ticked state.
TableCheck onToggle() => void—Runs on click, Enter or Space.
TableCheck labelstring—Accessible name, such as “Select Bore 12”.
TableCheck indeterminatebooleanfalseDash for a partial selection.

Tokens

TokenValueWhere it lands
--spacing-8 / -10 / -1232 / 40 / 48pxRow heights.
--spacing-312pxCell padding, left and right.
--font-size-md13pxCell values.
--font-size-sm12pxColumn headers.
--font-weight-medium475Headers and numbers.
--color-grey-100#F5F5F5Striped rows, header hover.
--color-grey-200#F1F1F1Row hover.
--color-primary-light#EBFAFFSelected row.
--color-primary-active#1877F2Selected bar, active caret, focus ring.
--global-neutral-grey-900#7F7D83Sort caret at rest.
--color-grey-300#ECECEDCell rules when bordered.
--color-border-default#E6E6E6Outer hairline and header rule.
--radius-lg8pxOuter corners.