Live example

Demo trips
Isla Calandra7€2,140
Kestrel Lakes4€990
Lumen City3€880
Mirrow Peaks5€1,620
Usage
<Table
  caption="Bookings"
  rows={bookings}
  getRowId={(b) => b.id}
  defaultSort={{ key: 'date', direction: 'descending' }}
  columns={[
    { key: 'title', header: 'Destination', rowHeader: true, sortValue: (b) => b.title },
    { key: 'amount', header: 'Value', align: 'end', sortValue: (b) => b.amount },
  ]}
/>

Props

Table props
PropTypeDefaultDescription
captionstringnoneNames the table and its scroll region.
columnsColumn<T>[]nonekey, header, render, sortValue, align, rowHeader, firstDirection.
rowsT[]noneComplete filtered row data, before sorting and limiting.
rowLimit?numbernoneDisplay this many rows after sorting all rows. Omit to display all.
getRowId(row: T) => stringnoneStable key per row.
defaultSort?SortState | nullnullInitial sort.
sort?SortState | nullnoneControlled sort.
onSortChange?(sort: SortState) => voidnoneCalled when a header is activated.
locale?string'en'Used for numeric-aware string sorting.
hideCaption?booleanfalseHide the caption visually.

Do and don't

Do

  • Mark the identifying column as rowHeader.
  • Right-align numbers and use tabular figures.
  • Start date and amount columns descending.

Don't

  • Use a table for layout.
  • Make whole rows clickable without a real link or button inside.

Accessibility

  • Sort controls are buttons inside column headers. Only the sorted column has aria-sort.
  • Sort changes are announced: "Sorted by Value, descending".
  • The scroll container is a focusable, labelled region so keyboard users can scroll wide tables on phones.

Keyboard

Table keyboard support
KeysAction
TabMoves between sort buttons and controls in cells
Enter, SpaceSorts by the focused column, or flips the direction
Arrow keysScroll the table when its region has focus