Skip to content

Design system

Documentation

DesignComponents / Table

Components / Table

Stable

Table

Use Table for genuinely tabular data where captions, headers, row relationships, and cell semantics help people compare records.

On this page

Examples

Semantic table

Compound parts preserve caption, headers, rows, and cells as native table elements.

Projects and their current owners.
ProjectOwnerStatus
Atlas onboardingMai NguyenActive
Orbit billingPhuc TranInvited
Lumen researchLinh DoPaused

Semantic Table preview

Density and sticky header

Use sm, md, or lg for row height and keep the header visible while the body scrolls.

CompactOwnerStatus
Atlas onboardingMai NguyenActive
Orbit billingPhuc TranInvited
DefaultOwnerStatus
Atlas onboardingMai NguyenActive
Orbit billingPhuc TranInvited
ComfortableOwnerStatus
Atlas onboardingMai NguyenActive
Orbit billingPhuc TranInvited

Table small, default, and large density with sticky header

Numeric alignment

Right-align numeric values while keeping the row and column meaning explicit.

MonthEventsChange
August18,420+12%
July16,445

Table with numeric columns

Loading, error, and empty

Request states stay inside the table boundary and explain the next action.

ProjectStatus
Loading projects…
ProjectStatus

No projects yet

Create a project to see it here.

Table loading, error, and empty states

Row selection

Checkboxes can compose with Table while select-all remains scoped to visible rows.

Project
Atlas onboarding
Orbit billing
Lumen research

Table with stable-ID row selection

Wide and long content

A surrounding scroll region protects narrow screens without changing table semantics.

Workspace recordOwnerLast updated
Workspace record with a deliberately long label 1A long owner name that remains readableToday
Workspace record with a deliberately long label 2A long owner name that remains readableToday
Workspace record with a deliberately long label 3A long owner name that remains readableToday
Workspace record with a deliberately long label 4A long owner name that remains readableToday
Workspace record with a deliberately long label 5A long owner name that remains readableToday

Wide Table inside a horizontal scroll region

When to use

Use Table when rows and columns have stable relationships. Use a list or Card composition when each item is a standalone task or when the content does not compare naturally across columns.

Usage

table-basic.tsx
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@phuctech/ui/components/table";

const rows = [
  ["Atlas onboarding", "Mai Nguyen", "Active"],
  ["Orbit billing", "Phuc Tran", "Invited"],
];

export function Example() {
  return (
    <div className="scrollbar-hover-reveal w-full overflow-x-auto rounded-lg border border-border">
      <Table className="min-w-[34rem]">
        <TableCaption>Projects and their current owners.</TableCaption>
        <TableHeader><TableRow><TableHead>Project</TableHead><TableHead>Owner</TableHead><TableHead>Status</TableHead></TableRow></TableHeader>
        <TableBody>{rows.map(([project, owner, status]) => <TableRow key={project}><TableCell className="font-medium">{project}</TableCell><TableCell>{owner}</TableCell><TableCell>{status}</TableCell></TableRow>)}</TableBody>
      </Table>
    </div>
  );
}

The component parts render native table elements. Put the horizontal overflow boundary around the table in a narrow layout; do not replace table semantics with a grid of divs.

Composition

  • TableCaption names the data set for assistive technology.
  • TableHead defaults to scope="col"; pass scope="row" for row headers.
  • TableRow selected exposes data-state="selected" for a selected row.
  • Use colSpan, aria-sort, and other native attributes directly on the relevant parts.

API

Component API
PropTypeDefaultDescriptionConstraints
Tabletable props + size"md"Semantic table root; size controls row and cell density (sm/md/lg).Wrap in overflow-x-auto when it can exceed the viewport.
TableHeaderthead props + stickyfalseHeader section; sticky keeps it visible while the body scrolls.Use sticky inside a bounded scroll region.
TableBody / TableFootersection propsNative table sections.Keep header and body structure valid.
TableRowtr props + selectedfalseData row with selected-state styling.Use stable row IDs in feature selection state.
TableHeadth props + scopescope=colColumn or row header cell.Set the correct scope for the relationship.
TableCelltd propsData cell with shared padding.Use colSpan for state rows.
TableCaptioncaption propsNames the table data set.Keep it visible or use sr-only when context is already clear.

Tokens

Component tokens
TokenRoleEffective valueUsage
--color-border-default / --color-bg-subtleTable boundariessemantic rolesSeparates rows and headers without heavy decoration.
--space-2 / 3 / 4Cell paddingdensity scaleThe size prop maps sm/md/lg to compact, default, and comfortable rows.
--motion-duration-normalRow state180msSoftens selected and hover transitions while respecting reduced motion.

Accessibility

  • Use a caption, column headers, and row headers where appropriate.
  • Keep selection checkboxes labeled with the row identity and scope select-all to the visible set.
  • Allow horizontal scrolling at narrow widths; do not clip long labels.
  • Keep loading, error, and empty feedback inside a stable table boundary.
  • Data table composes Table with staged filters, sorting, paging, and selection.
  • Checkbox supplies row selection controls.
  • Layout & Grid documents wide-content boundaries.