Table
Preview appears after the next Storybook update.
<Table />When to use
Use it for
- Records with the same attributes that users compare, sort or act on: tokens, components, pull requests.
- Sortable headers (sortDirection + onSort, or useTableSort) on columns users reorder by.
- Selectable rows: a Checkbox in the first cell, selected on the row, bulk actions above the table.
- Compact density for expert, data-heavy screens.
- Column widths when one column should not take the space of the others: width on the header cell, as a token length (var(--size-20)), a percentage or a fraction (0.35), plus minWidth for a floor. layout fixed keeps them exact while data loads.
- narrowLayout stack when people read the table on a phone: under about 480px each row becomes a card, every value beside its column name. Keep scroll (the default) for wide data people compare across rows.
Not for
- Layout: never use a table to position content.
- Fewer than three attributes per item: use a list.
- Editing many cells inline like a spreadsheet: that needs a grid pattern, not in the system yet.
Do and don't
Every example is rendered with the real component, so it always matches what ships.
| Button | 1,204 |
| Badge | 87 |
Right-align numbers
numeric aligns digits on the right with tabular figures, so values compare at a glance.
| No pull requests yet. Changes you sync from Figma appear here. |
Say what to do when it is empty
TableEmpty keeps the header in place and explains how rows will appear.
| color.signal.default |
Color as the only row state
A tinted row alone does not say selected. Pair it with a checked Checkbox.
Accessibility
- Native table, thead, th scope col and td: screen readers announce headers with each cell.
- Sortable headers contain a button; the th gets aria-sort ascending or descending on the sorted column only.
- Selected rows set aria-selected and use the selected surface plus a checked Checkbox (never color alone).
- The scroll container is a labelled region with tabIndex 0, so keyboard users can scroll wide tables (named by the caption or aria-label).
- Row height and cell padding follow density: 48/36px rows, both above the 24px target minimum for the controls inside.
- Stacked rows keep their table roles (table, row, cell), so screen readers still read cell by cell; the column names shown before each value are the header texts. The header row is hidden while rows stack, so put sorting somewhere else on phones.
Content guidelines
- Column headers are short nouns in sentence case: "Token", "Owner", "Uses".
- Units go in the header ("Size (px)") so cells hold only values.
- Empty cells show "None" or "n/a", never blank.
From the code Generated from the component, not editable here.
Anatomy and tokens
Read from packages/react/src/components/Table/Table.module.css at build time. Every value is a token: open one to see its alias chain in both themes.
- Scroll region (focusable, labelled)
.scroller - Scroll region (focusable, labelled), focus
.scroller:focus-visible - Table
.table - Caption
.caption - Header cell
.head - Cell
.cell - gridLines
.gridLines .cell + .cell, .gridLines .head + .head - Selection cell
.select - Row
.row - tbody, hover
@media (hover: hover) .tbody .row:hover - Row
.row[data-selected] - Sort button
.sortButton - Sort button, hover
@media (hover: hover) .sortButton:hover - Sort button, focus
.sortButton:focus-visible - Sort button
.sortButton[data-sorted] - Sort button
.sortButton[data-sorted] .sortIcon - Empty row
.empty - Stacked layout container (narrowLayout stack)
@container (max-width: 30em) .stack .tbody .row - Stacked layout container (narrowLayout stack)
@container (max-width: 30em) .stack .cell - Stacked layout container (narrowLayout stack)
@container (max-width: 30em) .stack .cell::before - Stacked layout container (narrowLayout stack)
@container (max-width: 30em) .stack .cell:is(:not([data-label]), [data-label=""])
Motion and interaction
Table animates on hover. Try it in the preview at the top.
| When | What changes | Duration | Easing |
|---|---|---|---|
| Hover | Background | motion.duration.instant75 ms | motion.easing.standardsettles in place |
With reduced motion turned on, every change happens at once.
From the code Generated from the component, not editable here.
Props
Parsed from the TypeScript types in packages/react/src/components/Table/Table.tsx, so this table cannot drift from the code. It also accepts every native <HTMLTableElement> attribute.
| Prop | Type | Default | Description |
|---|---|---|---|
caption | ReactNode | none | Visible caption above the table. Also names the scroll region. |
stickyHeader | boolean | false | Accessible name when there is no visible caption, for example "Tokens changed in this pull request". */ 'aria-label'?: string; /** Keep the header row visible while the table scrolls inside its container. |
maxHeight | string | none | Max height of the scroll container (CSS length from a token, for example var(--size-120)). Enables vertical scroll. |
gridLines | boolean | false | Hairlines between columns as well as rows. Default `false`. |
layout | 'auto' | 'fixed' | 'auto' | `auto` (default) sizes columns from their content and the widths you set. `fixed` follows the widths of the header cells exactly and truncates nothing on its own: long words wrap. Use `fixed` when rows should not shift as data loads. |
narrowLayout | 'scroll' | 'stack' | 'scroll' | In a container narrower than about 480px: `scroll` keeps the columns and scrolls sideways (default); `stack` shows each row as a card, every value beside its column name. Column names come from the header row, or from `label` on a cell. |
Comes with useTableSort, each accepting native <div> attributes.
Playground
Change the props and see the real Table update. Controls are generated from the TypeScript props; nothing here changes the system.
<Table caption="Tokens in this release" />Workbench
For contributors: every state with the accessibility panel, in the component workbench (Storybook). The stories double as visual test fixtures; the guidance on this page is the user documentation (open Table in Storybook).
Embed Storybook stories here (6)
Loads from https://bridge-storybook.floriangouloubi.com (set STORYBOOK_URL at build time). If Storybook is not running, the frames stay empty.
SortableAndSelectable
StickyHeader
ColumnWidths
StackedOnPhone
Empty
AllStates
From the code Generated from the component, not editable here.
Changelog
- New
Change drafts:
draftRequestText(component, kind, changes)anddraftPushDescription(entries, components)write a sentence-case title and a 1 or 2 sentence summary from detected changes only (no reason, no invented number, no em dash), withpushChangeRowsfor a change table of variable edits andcleanDraftTextto strip em dashesv1.0.0, Nov 11, 2026
- New
Data display: Card restyled as a flat plate with an
interactiveoption and padding sizes; Badge gainsappearance(subtle, now the default, or solid), a dot, icons and sizes (existing solid badges must passappearance="solid"to keep their look); new Avatar and AvatarGroup, Table (sortable headers with aria-sort, selectable rows, sticky header, focusable scroll region, useTableSort), Accordion, Tag (removable, toggleable), Divider and Kbdv1.0.0, Nov 11, 2026
- New
A new homepage at "/", ported from the validated test page: drag the bridge to keep what your exploration added; "Start it in Bridge", "Ship it everywhere at once" and "One system everyone can trust", each step drawn as an animated Field Guide illustration; "Decide in seconds" for the design system lead; "How Bridge compares" (a short table, every feature on demand); the questions teams ask; and the Horizon closing
v1.0.0, Nov 11, 2026
- Fix
Cloudflare Pages previews: read the HTML table Cloudflare really posts in its check run (
<strong>Preview URL:</strong>with an<a href>link, andBranch Preview URL:on preview branches), not only Markdown rowsv1.0.0, Nov 11, 2026
- Fix
Component specs count height and width as the content box (a bordered table cell is its height plus its border), the Figma build draws an empty sized slot at its padding instead of 100px, and stories reach every Figma variant: a disabled control on IconButton, Input, Select, Combobox and Textarea, and Figma variant stories for AccordionItem, MenuItem, MenuCheckboxItem, MenuSubmenu and TableEmpty
v1.0.0, Nov 11, 2026
- Fix
Studio: the Tokens table fills its width again
v1.0.0, Nov 11, 2026
- Fix
Studio Components: the Open requests column leaves the table (teams triage requests in Jira); the component drawer still counts them with a link to GitHub
v1.0.0, Nov 11, 2026
- Fix
Studio: screens no longer end with the "Updated" date and "View source" line (they show live data; Library pages keep it), and the Components table drops its note under the table
v1.0.0, Nov 11, 2026
- Fix
Studio Tokens: a page header and one toolbar row (filter field "Filter N tokens", type chips with counts, one Options menu for collections, usage, brand and modes, columns and exact match, then New token), and the table at full width with Name, Light and Dark
v1.0.0, Nov 11, 2026