Skip to main content

Storybook

Table

Displays structured data in rows and columns with support for sorting, pagination, and row selection. Use for dense data sets that require comparison or scanning. Don't use for simple key-value data.

Figma Storybook Docs

Description

What it does

Presents tabular data in a structured grid of rows and columns, with optional sorting controls, pagination, row selection, and inline actions. Supports both read-only display and interactive data management workflows.

Where it appears

Stakeholder lists, consultation response summaries, submission logs, audit trails, and any screen that requires scanning or comparing multiple records side by side.

Why it exists

Government workflows frequently involve reviewing large sets of structured records — stakeholder registers, public submissions, engagement histories. The table component provides a consistent, accessible pattern for these dense data contexts without requiring teams to build custom grids.

Dependencies

Checkbox, Badge, Button, Pagination

Anatomy

) with data cells (
PartRequired?Notes
Table container Required Wraps the full table. Sets overflow, border, and radius. Scroll behaviour is horizontal on narrow viewports.
Caption Recommended A visually hidden or visible label describing the table's content. Required for screen reader context. Maps to the HTML
element.
Column header row (thead) Required Contains cells. Provides column labels. May include sort controls.
Sort control Optional An icon button inside a that triggers ascending/descending sort on that column. Communicates sort state via aria-sort.
Table body (tbody) Required Contains data rows (
). Each row maps to one record.
Row header cell Optional A cell in the first column that names the record. Improves navigation for screen readers scanning row by row.
Row selection checkbox Optional A checkbox in the first column allowing single or bulk row selection. Header row includes a select-all checkbox.
Status badge Optional An inline Badge component within a cell used to communicate record status (e.g. Active, Pending, Archived).
Inline action Optional Icon buttons or a dropdown at the end of a row for record-level actions (Edit, View, Delete). Should be limited to one to three actions.
Empty state row Recommended A full-width cell displayed when the table has no data. Explains why the table is empty and what the user can do.
Pagination bar Optional A Pagination component rendered below the table to step through large data sets. See the Pagination component for full guidance.
Table footer (tfoot) Optional Used for aggregate values such as totals or counts. Rendered below tbody.

Variants

Default (read-only)

Static display of structured records with no interactive affordances beyond scrolling.

When users only need to scan or read data, with no need to sort, select, or act on individual rows. Suitable for audit logs and read-only summaries.

Sortable

Column headers are interactive controls that sort the data set ascending or descending.

When users need to reorder records by a meaningful attribute, such as submission date, name, or status.

Selectable

Each row has a checkbox allowing individual or bulk selection, enabling bulk actions on the selected set.

When users need to perform batch operations — bulk export, bulk status change, bulk assignment — on a subset of records.

With inline actions

Each row contains one to three action controls (icon buttons or an overflow menu) scoped to that record.

When the primary workflow is acting on individual records from the list view rather than opening a detail page.

Striped

Alternating row backgrounds improve scanability for very wide or dense tables.

When column count is high (six or more columns) or cell content is long, making it difficult to track a row across the full width.

Compact

Reduced vertical cell padding to show more rows in the visible area.

When the data set is large and users need to compare many rows simultaneously, and cell content is short (single-line values).

States

State Behaviour
Default All rows render with standard background (--op-color-bg-primary) and default border (--op-color-border-default).
Row hover The hovered row background shifts to --op-color-bg-subtle to indicate focus of attention. Does not affect sort or selection state.
Row selected Selected rows receive a left border accent (--op-color-interactive-default) and a tinted background (--op-color-interactive-default at low opacity). The row checkbox is checked.
Column sort ascending The sort icon in the active column header points upward. aria-sort="ascending" is set on the . Data is ordered from lowest to highest.
Column sort descending The sort icon points downward. aria-sort="descending" is set on the . Data is ordered from highest to lowest.
Loading A skeleton placeholder replaces tbody rows. The caption or a visually hidden live region announces that data is loading.
Empty The table renders with headers intact and a single full-width row containing the empty state message. Do not hide the table entirely.
Error An inline error message replaces tbody, explaining the failure and providing a retry action. Table headers remain visible.
Disabled row Rows that cannot be acted upon are rendered at reduced opacity (0.5). Their checkboxes are disabled. Tooltip explains why the row is unavailable if the reason is non-obvious.

Usage guidelines

When to use

  • Displaying a list of stakeholders with attributes such as name, organisation, status, and last contacted date.
  • Showing public consultation submissions with columns for submitter, submission date, topic, and review status.
  • Presenting an engagement activity log where users need to sort by date or filter by type.
  • Bulk-managing records, such as reassigning a group of stakeholders to a new project officer.
  • Comparing structured data across multiple records where alignment by column is meaningful.

When not to use

  • For simple key-value metadata about a single record — use a Description List instead.
  • For a small set of cards or tiles where visual hierarchy matters more than column alignment — use a Card List.
  • For calendar or timeline data where temporal position is the primary axis — use a Timeline or Calendar component.
  • For a single flat list where there is only one meaningful attribute — use a List component.

Do / Don't

Do

Always include a visible or visually hidden so screen readers can identify the table before entering it.

Don't

Don't rely on surrounding heading text alone to label the table — headings are not announced when a user navigates directly to the table.

Do

Use for column headers and for row headers to give screen readers the correct association.

Don't

Don't use for header cells or omit scope attributes, as this breaks row-column associations for assistive technology.

Do

Keep column count to a maximum of seven or eight on desktop. Prioritise the most important attributes and use a detail view or expandable row for secondary data.

Don't

Don't add more columns to avoid building a detail page — overly wide tables break on small screens and overwhelm users.

Do

Show meaningful empty state copy when no records match the current filters, explaining what happened and how to adjust.

Don't

Don't render an empty tbody with no explanation — users cannot tell whether the data is missing, filtered out, or still loading.

Do

Place bulk action controls (e.g. Export selected, Change status) directly above or below the table, activated only when rows are selected.

Don't

Don't trigger destructive bulk actions immediately on selection — always require an explicit confirm step.