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
- React / Web Components: a semantic
<table>with<caption>,scope="col"headers, andaria-sorton sortable headers (none/ascending/descending). Selection uses the GHDS Checkbox with accessible labels (“Select row …”, “Select all rows”). Web Components dispatchessort-changeandselection-change. - React Native: rows are flex
Boxes withrole="table"/row/columnheader/cell; sort is a header button and selection arole="checkbox"control.aria-sortis a documented web-only gap. - See the Accessibility Guide.
| 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
| Prop | Type | Default | Description |
|---|---|---|---|
columns | TableColumn[] | — | { key, header, sortable?, align? } per column. |
rows | TableRow[] | — | { id, [key]: ReactNode } — rendered in the given order. |
caption | string | — | Accessible caption. |
sort / onSortChange | TableSort / (sort) => void | — | Controlled sort + intent callback. |
selectedIds / onSelectionChange | string[] / (ids) => void | — | Controlled selection (shows checkboxes). |
Web Components@ghds/web-components
| Prop | Type | Default | Description |
|---|---|---|---|
columns / rows | GhTableColumn[] / GhTableRow[] | — | Set as properties. |
caption | string | — | Accessible caption. |
sort | GhTableSort | — | Current sort (property). |
selectedIds | string[] | — | Selected ids (property); enables checkboxes. |
sort-change / selection-change | CustomEvent | — | Dispatched intents. |
React Native@ghds/react-native
| Prop | Type | Default | Description |
|---|---|---|---|
columns / rows | TableColumn[] / TableRow[] | — | Same shape as React. |
caption | string | — | Accessible name. |
sort / onSortChange | TableSort / (sort) => void | — | Controlled sort + intent callback. |
selectedIds / onSelectionChange | string[] / (ids) => void | — | Controlled selection. |
testID | string | — | Test 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}
/>;