Table

A hand-drawn data table with sortable headers and row selection.

Overview

A Table presents rows of structured data with sortable columns and optional row selection. Sorting and selection are controlled — the table renders the rows you give it (in the order you give them) and emits intent via onSortChange / onSelectionChange.

Anatomy

A sketchy outline (@ghds/sketch-core) frames a semantic <table> (flex rows on React Native). Sortable headers are buttons carrying aria-sort; an optional leading checkbox column (with a select-all header) drives selection. This is the data foundation for the M12 data-state patterns.

Variants & States

State Status Notes
Sortable column Implemented Header button toggles asc/desc; aria-sort reflects it (web).
Row selection Implemented Opt-in checkbox column + select-all with an indeterminate state.
Selected row Implemented Highlighted background.

Usage

Do

  • Give each row a stable id and provide the rows already sorted (the table is controlled).
  • Provide a caption for the accessible name.
  • Right-align numeric columns with align="right".

Don't

  • Don't expect the table to sort or paginate data for you — wire onSortChange to your data.
  • Don't put huge datasets in one table without pagination or virtualization.

Accessibility

Key Action
Tab Move between sortable headers, selection checkboxes, and interactive cells.
Enter / Space Activate a header (sort) or toggle a checkbox.

Content

Concise column headers and cell values. Represent booleans/status with a Badge inside a cell.

Props API

React@ghds/react

PropTypeDefaultDescription
columnsTableColumn[]{ key, header, sortable?, align? } per column.
rowsTableRow[]{ id, [key]: ReactNode } — rendered in the given order.
captionstringAccessible caption.
sort / onSortChangeTableSort / (sort) => voidControlled sort + intent callback.
selectedIds / onSelectionChangestring[] / (ids) => voidControlled selection (shows checkboxes).

Web Components@ghds/web-components

PropTypeDefaultDescription
columns / rowsGhTableColumn[] / GhTableRow[]Set as properties.
captionstringAccessible caption.
sortGhTableSortCurrent sort (property).
selectedIdsstring[]Selected ids (property); enables checkboxes.
sort-change / selection-changeCustomEventDispatched intents.

React Native@ghds/react-native

PropTypeDefaultDescription
columns / rowsTableColumn[] / TableRow[]Same shape as React.
captionstringAccessible name.
sort / onSortChangeTableSort / (sort) => voidControlled sort + intent callback.
selectedIds / onSelectionChangestring[] / (ids) => voidControlled selection.
testIDstringTest handle for queries.

Code examples

// React — @ghds/react
import { Table, type TableSort } from '@ghds/react/table';

<Table
  caption="Contributors"
  columns={[
    { key: 'name', header: 'Name', sortable: true },
    { key: 'commits', header: 'Commits', sortable: true, align: 'right' },
  ]}
  rows={rows}
  sort={sort}
  onSortChange={setSort}
  selectedIds={selected}
  onSelectionChange={setSelected}
/>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
  import '@ghds/web-components/table';
</script>
<gh-table id="my-table" caption="Contributors"></gh-table>
<script>
  const el = document.getElementById('my-table');
  el.columns = [
    { key: 'name', header: 'Name', sortable: true },
    { key: 'commits', header: 'Commits', sortable: true, align: 'right' },
  ];
  el.rows = [{ id: '1', name: 'Ada', commits: 128 }];
  el.selectedIds = [];
  el.addEventListener('sort-change', (e) => { el.sort = e.detail; });
  el.addEventListener('selection-change', (e) => { el.selectedIds = e.detail; });
</script>
// React Native — @ghds/react-native
import { Table } from '@ghds/react-native/table';

<Table
  caption="Contributors"
  columns={[{ key: 'name', header: 'Name', sortable: true }]}
  rows={[{ id: '1', name: 'Ada' }]}
  sort={sort}
  onSortChange={setSort}
/>;

Live Demo

React @ghds/react

Web Components @ghds/web-components