Launch prototype

Components

Table

Two layers on React Aria's Table: a plain Table primitive for a simple list, and TableCard - the batteries-included data table with row selection, sortable columns, a tooltip'd header, and row-actions dropdown.

Component Playground

Build a table from the pieces below - size, row count, selection, sorting, which columns show, and row actions - all wired to the real TableCard/Table from components/application/table/table.tsx.

Team members

3

Everyone with access to this workspace.

Name
Status
Candice Wucandice@untitledui.com
Inactive
Demi Wilkinsondemi@untitledui.com
Active
Lana Steinerlana@untitledui.com
Inactive

Page 1 of 4

Controls

Size

Rows

Columns

Behaviour

Table (primitive)

components/base/table/table.tsx - just Table/TableHeader/Column/TableBody/Row/Cell, styled with this system's tokens. Use it when you don't need selection, sorting, or a card wrapper - a Row can take href to make the whole row a link.

Basic table

ProjectOrganisationStatus
Adelaide Hills Bushland SurveyAdelaide Hills LandcareActive
Coorong Wetlands Bird CountBirds SAUnder review
Flinders Ranges Reptile AtlasDEW Biodiversity TeamDraft

Data table (TableCard)

components/application/table/table.tsx - wrap rows in TableCard.Root for the card chrome, add a TableCard.Header, and use Table/Table.Header/Table.Head/Table.Row/Table.Cell for the grid itself. Set selectionMode="multiple" on Table to get a selection checkbox column for free - it composes the same Checkbox used everywhere else. The Name column below is clickable (allowsSorting) but this specific example doesn't actually reorder rows - React Aria's Table has no idea how to compare your data, so allowsSorting alone only renders the chevron and makes the header interactive. Sorting only works once you also supply sortDescriptor/onSortChange and re-order items yourself - see the Playground above for a real, working example.

Team members

Team members

2

Everyone with access to this workspace.

Name
Status
Olivia Rhyeolivia@untitledui.com
Active
Phoenix Bakerphoenix@untitledui.com
Inactive

Page 1 of 5

TableRowActionsDropdown is a ready-made Edit/Copy link/Delete menu built on Dropdown - see that page for building a custom one. Figma's own "Team members" reference documents two other row-action treatments this system doesn't have a ready-made component for yet - bare "Action icons" (a row of icon-only buttons, no dropdown) and "Action buttons" (text links). Both are real, valid Untitled UI patterns; only the dropdown one has been built here so far.

Numbered pagination

TableCard.PaginationNumbered is the richer footer Figma's Sales example shows: a Rows per page control, Previous/Next, a page list that always keeps the first and last three pages (1 2 3 … 8 9 10), and a "1-50 of 250" summary. It sits alongside TableCard.Pagination rather than replacing it, so every table using the simple "Page X of Y" footer is unaffected. The row slicing is the caller's job - it only reports the page and page size.

The footer does not move as its numbers change. Every figure in it (the page numbers, the rows-per-page value, the range) is set in tabular figures (tabular-nums: Barlow's own fixed-width digits, not a monospace font, so a 1 and an 8 take the same room and the letters are unchanged), and the layout is three columns (1fr auto 1fr) so the page numbers stay centred, with the range in a fixed minimum width so Next stays where it is.

TableCard.PaginationNumbered

Rows per page
1 - 50 of 250

Status badges

Figma's table examples use two different status treatments depending on what the status actually means - BadgeWithDot for a simple on/off state (Active/Inactive), BadgeWithIcon for an outcome (Paid/Refunded/Cancelled). Both are real, already-shipped Badge variants - picking the wrong one for the context is the mismatch to avoid, not a missing component.

BadgeWithDot (on/off) / BadgeWithIcon (outcome)

ActiveInactive
PaidRefundedCancelled

Usage

// Plain table
import { Table, TableHeader, Column, TableBody, Row, Cell } from "@/components/base/table/table";

<Table aria-label="Projects">
  <TableHeader>
    <Column isRowHeader>Project</Column>
    <Column>Status</Column>
  </TableHeader>
  <TableBody items={projects}>
    {(project) => (
      <Row id={project.id} href={project.href} textValue={project.name}>
        <Cell>{project.name}</Cell>
        <Cell><Badge color={project.statusColor}>{project.status}</Badge></Cell>
      </Row>
    )}
  </TableBody>
</Table>

// Data table with selection + row actions
import { Table, TableCard, TableRowActionsDropdown } from "@/components/application/table/table";

<TableCard.Root>
  <TableCard.Header title="Team members" badge={String(members.length)} />
  <Table aria-label="Team members" selectionMode="multiple">
    <Table.Header>
      <Table.Head id="name" isRowHeader allowsSorting>Name</Table.Head>
      <Table.Head id="actions" />
    </Table.Header>
    <Table.Body items={members}>
      {(row) => (
        <Table.Row id={row.id}>
          <Table.Cell>{row.name}</Table.Cell>
          <Table.Cell><TableRowActionsDropdown /></Table.Cell>
        </Table.Row>
      )}
    </Table.Body>
  </Table>
</TableCard.Root>

API

ComponentNotes
Table (base)React Aria TableProps. Composes TableHeader/Column/TableBody/Row/Cell.
TableCard.Rootsize ("xs" | "sm" | "md", default "md") - propagates to the Table inside it via context, and wins over a size passed only to the nested Table. "xs" is the dense 34px header / 44px row size from Figma's results-table reference.
TableCard.Headertitle, badge (string or element), description, contentTrailing.
TableCard.Paginationpage, pageCount, onPageChange - the simple "Page X of Y" + Previous/Next footer.
TableCard.PaginationNumberedpage (1-indexed), pageCount, onPageChange, pageSize, onPageSizeChange, totalCount, pageSizeOptions (default 10 / 25 / 50 / 100), className - Rows per page, Previous/Next, a 1 2 3 … 8 9 10 page list and a "1-50 of 250" summary. The page list builder is also exported as tableCardPaginationRange.
Table (application)React Aria TableProps + size. Table.Header/Table.Head/Table.Row/Table.Cell auto-render a selection checkbox column when selectionMode is set. sortDescriptor/onSortChange are required to make allowsSorting actually reorder rows - the Table has no built-in comparator.
Table (application) - sortDescriptor / onSortChangeSorting cycles through three states per column: the first click sorts one way, the second the other way, and the third resets to the sort the table opened with (its default). The default is the first sortDescriptor the table receives, so callers pass nothing extra. Clicking the default column only flips its direction. The caller still re-orders its own rows.
Table (application) - bodyScrollableOpt-in, default false. Makes the table's own wrapper scroll vertically (min-h-0 flex-1 overflow-y-auto) instead of growing to its full content height - use it only when the table's container already has a fixed height, so a toolbar and pagination can stay on screen while only the rows scroll. Pair with Table.Header sticky. Required on every collection screen (CONTRACTS.md 4.2, the table fits the viewport); a table embedded in a detail tab keeps its natural height.
Table (application) - layout"auto" (default) or "fixed". Fixed lays the columns out from the header row alone, so a column keeps its width whatever rows are showing; auto sizes each column to its widest cell, so a filter, search, sort or the next page can move every column. Required on every collection table (CONTRACTS.md 4.2f): give every Table.Head a width (a percentage for a list that fills the screen, pixels for a wide report), put a min-w on the table so a narrow window scrolls it sideways, and let a long value wrap or truncate with its full text as the tooltip. A column without a width leaves the space unused.
Table.Header - stickyOpt-in, default false. Pins the header row to the top of a scrolling table (sticky top-0). A no-op when the table is not inside a scrolling ancestor.
Table.Headlabel, tooltip (renders a help-icon Tooltip next to the label), plus Column's allowsSorting/isRowHeader.
Table.RowhighlightSelectedRow (default true), size override.
TableRowActionsDropdownNo props - a fixed Edit/Copy link/Delete menu. Build a custom one from Dropdown for other actions.

Figma

Application Components - Tables documents header cells (help-icon tooltip, sort chevron, checkbox), the full cell-type catalog (text, avatar, file type icon, badge, badges-multiple, trend, avatar group, select dropdown, progress bar, star rating, and three row-action treatments), and full table examples (Team members, Sales, Companies, Files - both divider-line and alternating-fill row styles, desktop and mobile). This page's "Data table" example mirrors Figma's own "Team members" reference; TableCard.Pagination mirrors the Previous/Next footer every one of those examples ships with.

Not yet built, found auditing against the fuller examples above - real, valuable, and each a separate piece of work rather than a style fix, so logged here instead of built speculatively: