Tables

Rows sharing one schema: Table (React Aria’s grid), TableCard (the card shell), and TableRowActionsDropdown. Two sizes, set on the card.

Demo

The two parts the sheet gives their own symbol set: every cell type, and the header cell, both on the size axis.

Olivia Rhye
Olivia Rhyeolivia@untitledui.com
Component
Cell type
size
Lead action type
// Avatar — Figma 1224:5904. // Avatar at size md or sm, 12px to the text at md and 8 at sm. // The sheet's symbol is 320px wide at md, lead action included. // The sheet draws the lead action inside the cell — a checkbox. // A real table is different: selectionMode inserts a selection // column of its own, and only ever a checkbox. A radio or a toggle // is a column you place yourself. <Table.Cell> <div className="flex items-center gap-3"> <Checkbox aria-label="Select row" /> {/* A person: a 40px avatar at md, 32 at sm, with the name over a handle or an email. */} </div> </Table.Cell>

Parts here, whole tables in the contextual demo

The sheet draws two symbol sets for the inside of a table — Table cell (1222:107305, fifteen Types over two sizes and two states) and Table header cell (1221:107933, three shapes over two sizes) — and this Demo is those two, nothing more. An assembled table is a composition rather than a component: it is a card header, a filters band, the table, and a pagination footer, and it lives on the contextual demo where it can be a real screen.

The controls are the component's own properties

Read off the Figma component rather than inferred from its symbols: beside type, size and state, Table cell exposes three booleans — leadAction, leadText and supportingText — and each Type wires up a subset. The four identity Types take all three; Avatar group and both Trends take supportingText alone, where it is the “10 users” count and the “$1,800” value. The other eight take none: the “+4” on Badges multiple, the “60%” beside a progress bar and the third and fourth action icon are drawn into the symbol, so the rail does not offer to remove them.

A lead action is one of three controls

The cell holds an instance of Table cell lead action (9400:570197), which is its own set of three — a checkbox, a radio, or a toggle — so the Demo switches between them. A real table is the other way round: selectionMode inserts a selection column the kit owns, and it draws a checkbox and nothing else. A radio or a toggle is a column you place yourself, which is what the Lead actions section below is about.

The header cell is white on the sheet, gray here

Table header cell (1221:107933) draws itself on bg-primary, but every composed table on the sheet has a bg-secondary header row — sampled at #f8fafc on Team members, Customers and Files, and white only on the alternating-fills variant. The kit draws the default, so the specimen is gray. Everything else matches to the pixel: 152 / 104 / 68 wide at md and 144 / 96 / 68 at sm, 44 and 40 tall, 24/12 and 20/8 paddings, a 12px semibold text-quaternary label 4px from a 12px arrow.

The lead action is not part of the header cell

The sheet folds the selection checkbox into the first header cell; the kit gives selection a column of its own, which Table.Header inserts when selectionModeis multiple. The two render the same row and disagree about how many cells it has — which is why the sheet’s first column measures 44px wider than the one you declare. The column widths section has the correction.

Contextual demo

Six working screens: the sheet’s four compositions, an empty result set, and a table wider than its column.

This page is also a consumer

Every props table and token table on this docs site renders through this kit: the site’s DataTable wraps Table at size="md", in _components/prose.tsx.

The family

Three exports; the root carries Header, Head, Row, Cell, and Body, and TableCard carries Root and Header.

The components in this family, with their descriptions and import paths
ComponentDescriptionImport from
TableThe root: React Aria’s Table inside a keyboard-focusable scroll region. Selection mode, sorting state, and the accessible name live here. Its own size prop is shadowed by the card context — see the Code tab’s defects.@/​components/​application/​table/​table
Table.HeaderThe header row: bg-secondary with a hairline divider drawn as a pseudo-element, and the selection column inserted on its own when selection is on.@/​components/​application/​table/​table
Table.HeadOne column header: a label, an optional named help tooltip, and the sort glyphs when the column allows sorting. Reads its size from the context only.@/​components/​application/​table/​table
Table.RowOne row: 72px at md and 64px at sm, hover and selected both painted bg-secondary, with the divider under every row — the last one included.@/​components/​application/​table/​table
Table.CellOne cell: text-sm in text-tertiary with the size’s paddings. Everything inside it — avatars, badges, progress, actions — is caller composition.@/​components/​application/​table/​table
Table.BodyReact Aria’s TableBody re-exported untouched: static children or an items collection, plus renderEmptyState, with no kit styling of its own.@/​components/​application/​table/​table
TableCard.RootThe card shell: bg-primary, a 12px radius, shadow-xs, and a 1px inset outline. It owns the size context every part reads, which makes it the one place the size axis works family-wide.@/​components/​application/​table/​table
TableCard.HeaderThe card’s title bar: an h2 title, an optional badge and description, and a trailing slot pinned top-right at every width.@/​components/​application/​table/​table
TableRowActionsDropdownA fixture, not a configurable component: a dots button opening a fixed three-item menu (Edit, Copy link, Delete). Build your own Dropdown for anything else.@/​components/​application/​table/​table

Built from

Five pieces make up the family; four have their own docs page.

Component
Where it’s used here
CheckboxThe selection column, at size md — a 20px box — with slot="selection", which is how React Aria names it “Select” per row and “Select All” in the header.
BadgeTableCard.Header’s badge prop when given a string: gray, modern, sm, 6px after the title.
TooltipTable.Head’s tooltip prop: a 16px help icon in a TooltipTrigger, named “More information” since 2026-08-23.
DropdownTableRowActionsDropdown: Dropdown.Root, DotsButton, Popover, and Menu around three fixed items.

Sizes

Two sizes on TableCard.Root, read through context: md is 44px over 72px rows at 24px insets; sm 40px over 64px at 20px.

Team members

3 members

Manage the people in this workspace.

Name
Role
Status
Olivia Rhye
Olivia Rhye@olivia
Product designerActive
Phoenix Baker
Phoenix Baker@phoenix
Product managerActive
Lana Steiner
Lana Steiner@lana
Frontend developerActive
size md — 44px header, 72px rows, cells px-6 py-4

Team members

3 members

Manage the people in this workspace.

Name
Role
Status
Olivia Rhye
Olivia Rhye@olivia
Product designerActive
Phoenix Baker
Phoenix Baker@phoenix
Product managerActive
Lana Steiner
Lana Steiner@lana
Frontend developerActive
size sm — 40px header, 64px rows, cells px-5 py-4

The card is the size switch

TableCard.Root size="sm" resizes everything inside it — header and row heights, cell and card-header paddings, the selection column — because every part reads the card’s context. Header, Row, and Cell also take their own size for row-level exceptions, but Head does not, and the root’s size prop never takes effect at all.

Cell types

Fifteen types, both sizes. The kit ships none of them — a cell is a container, and each one is composed from kit parts.

Cell type
The composition
TextIdentity
Olivia Rhyeolivia@untitledui.com
AvatarIdentity
Olivia Rhye
Olivia Rhyeolivia@untitledui.com
File type iconIdentity
Tech requirements.pdf200 KB
Payment methodIdentity
Visa ending in 1234Expiry 06/2028
BadgeStatus
Active
Badges multipleStatus
LabelLabelLabel+4
Trend positiveData
$1,800Up 20%
Trend negativeData
$1,800Down 20%
Progress barData
60%
Star ratingsData
4 out of 5
Avatar groupData
10 users
Olivia Rhye
Phoenix Baker
Lana Steiner
Demi Wilkinson
Candice Wu
+5
Select dropdownControls
Action buttonsActions
Action iconsActions
Action dropdown iconActions
size md — a 72px row at px-6, with 40px avatars and 12px between an object and its text
Cell type
The composition
TextIdentity
Olivia Rhyeolivia@untitledui.com
AvatarIdentity
Olivia Rhye
Olivia Rhyeolivia@untitledui.com
File type iconIdentity
Tech requirements.pdf200 KB
Payment methodIdentity
Visa ending in 1234Expiry 06/2028
BadgeStatus
Active
Badges multipleStatus
LabelLabelLabel+4
Trend positiveData
$1,800Up 20%
Trend negativeData
$1,800Down 20%
Progress barData
60%
Star ratingsData
4 out of 5
Avatar groupData
10 users
Olivia Rhye
Phoenix Baker
Lana Steiner
Demi Wilkinson
Candice Wu
+5
Select dropdownControls
Action buttonsActions
Action iconsActions
Action dropdown iconActions
size sm — a 64px row at px-5, with 32px avatars and 8px gaps; the compositions themselves do not change

Sixty symbols, thirty rows

The sheet draws each Type twice over — a Default and a Hover/selected — which is sixty symbols for fifteen Types. Nothing inside a Type changes between the two: the row paints bg-secondary for both states, which is why the two galleries above cover the whole set. Hover any row to see it. Every composition is built from the kit part it names on the recipes table — a change in Badge or Avatar reaches this page without an edit here.

Column header labels

A label, an optional help icon, and four sort states. The kit derives the arrow from React Aria rather than a prop.

Company
Company
No arrow — allowsSorting is off. The column is a label and nothing more.
Company
Company
Chevron selector — allowsSorting is on and this column is not the sorted one.
Company
Company
Arrow down — sortDescriptor names this column, direction descending.
Company
Company
Arrow up — The same column ascending — the kit rotates the down arrow 180°.

Sixteen symbols, eight live cells

The sheet’s _Table header label set is a help icon crossed with four arrows crossed with a hover state. Hover is live on every cell above, so the eight here cover all sixteen. One naming difference: the sheet draws a separate arrow-up glyph, and the kit rotates the down arrow 180° — identical on screen. Both glyphs are aria-hidden since 2026-08-23, because React Aria’s aria-sort already says which way the column runs, and the help trigger is named “More information”.

Lead actions

The control that opens a row. The kit inserts a checkbox for you; the sheet’s other two are yours to place.

Control
Type
What it means
CheckboxSelect many rows. The only one the kit inserts for you, at selectionMode multiple.
RadioPick exactly one row. selectionMode single still draws a checkbox, so place this yourself.
ToggleSwitch the row's own state — enabled, published, live. Not selection at all.

Only one of the three is automatic

selectionMode="multiple" makes Table.Header and Table.Row insert the checkbox column themselves, sized and padded per the sheet, with React Aria naming the boxes “Select” and “Select All”. selectionMode="single" still draws a checkbox rather than a radio, so a single-choice table needs a column of your own — and a toggle is not selection at all, it is the row’s own state.

Column widths

Every composition is a fixed layout over the 1216px frame. The identity column leads, but never takes more than a third.

28.5%
9%
14.7%
17.1%
23.4%
7.4%
Name 346pxStatus 109pxRole 179pxEmail address 208pxTeams 284pxActions 90px
Team members — identity column 28.5% of the frame
25.7%
12.5%
26.3%
13.2%
18.1%
4.3%
Company 312pxStatus 152pxAbout 320pxUsers 160pxLicense use 220pxActions 52px
Customers — identity column 25.7% of the frame
14.5%
14.5%
14.5%
27.7%
18.1%
10.8%
Invoice 176pxDate 176pxStatus 176pxCustomer 337pxPurchase 220pxActions 131px
Invoices — identity column 14.5% of the frame
37.8%
14.5%
14.5%
14.5%
14.5%
4.3%
File name 460pxFile size 176pxDate uploaded 176pxLast updated 176pxUploaded by 176pxActions 52px
Files uploaded — identity column 37.8% of the frame

table-fixed is what makes a width bind

Read off the sheet with get_metadata, so these are the frame’s own auto-layout widths rather than anything measured from a screenshot. Reproducing them takes table-fixed: under a table’s default auto layout a browser treats a width as a hint and re-solves it from the content, which is how one className="w-full" column ends up taking three-quarters of the row. The Guidelines tab has the rule and what it looks like when it goes wrong.

Selection and sorting

Selection inserts its own checkbox column; sorting is a controlled SortDescriptor. The demo holds real product state.

Team members

5 members

Manage the people in this workspace.

Name
Role
Status
Candice WuFullstack developerActive
Demi WilkinsonBackend developerInvited
Lana SteinerFrontend developerActive
Olivia RhyeProduct designerActive
Phoenix BakerProduct managerActive
selectionMode multiple, sortable Name and Role, a help tooltip on Status, and a row-actions menu

Selected is hover’s twin, on purpose

A hovered row and a selected row both paint bg-secondary: the sheet folds them into one combined Hover/selected state, and the kit follows. The checkbox and aria-selected carry the real state. The last row keeps its divider since 2026-08-23: on the sheet it separates the rows from the card’s pagination footer, which the compositions above now show.

Alternating fills

Not a variant: the header goes bg-primary, odd rows take bg-secondary, and the line dividers stay. One className each.

Member
Role
Teams
Status
Olivia Rhye
Olivia Rhye@olivia
Product designer
DesignProduct+4
Active
Phoenix Baker
Phoenix Baker@phoenix
Product manager
ProductMarketing+2
Active
Lana Steiner
Lana Steiner@lana
Frontend developer
Engineering+3
Active
Demi Wilkinson
Demi Wilkinson@demi
Backend developer
Engineering
Invited
Header className bg-primary, Row className odd:bg-secondary; dividers kept

A Figma-only variant

The kit implements the sheet’s default, Divider line, and matches the variant it implements — the standalone header symbol’s bg-primary belongs to this alternating variant, not to the default. On odd rows the hover tint disappears into the stripe, which is one reason the kit leaves this as a caller composition.

Overflow

The sheet’s mobile answer is horizontal overflow. The kit wraps every table in a keyboard-reachable scroll region.

Name
Teams
Usage
Trend
Olivia Rhye
Olivia Rhye@olivia
DesignProduct+4
82%
Up 12%
Phoenix Baker
Phoenix Baker@phoenix
ProductMarketing+2
64%
Up 8%
Lana Steiner
Lana Steiner@lana
Engineering+3
47%
Down 3%
A wide table in a 384px column: Tab to the region — it draws the kit focus ring — then scroll with the arrow keys

Named after the table

The region takes role="region" and derives its name from the table’s: aria-label of "Team usage" becomes "Team usage (scrollable)", with "Scrollable table" as the unnamed fallback.

Row actions

Three patterns from the sheet: two named buttons, a strip of icon buttons, and the overflow menu. Pick one per table.

Name
Buttons
Icons
Overflow
Demi Wilkinson
Demi Wilkinson@demi
CW
Candice Wu@candice
Natali Craig
Natali Craig@natali

Every icon needs its own name

Four unnamed pencils in a table say nothing about which row they edit, so each button above is named after its row — Edit Demi Wilkinson, not Edit. The same rule is why TableRowActionsDropdown took a label on 2026-08-26: without it every row’s menu announced “Open menu”. The sheet’s icon cell draws four glyphs; its Team members composition uses two of them.