1. Documentation
  2. Data display
  3. Table
ReadmenpmGitHub
  • Introduction
  • Installation
  • Theming
  • Icons
  • Accordion
  • Card
  • Scroll Area
  • Separator
  • Sidebar
  • Field
  • Label
  • Input
  • Textarea
  • Number Field
  • OTP Field
  • Select
  • Combobox
  • Checkbox
  • Radio
  • Switch
  • Slider
  • Toggle
  • Calendar
  • Date Picker
  • File Upload
  • Button
  • Copy Button
  • Dropdown
  • Command
  • Tabs
  • Breadcrumb
  • Pagination
  • Tree
  • Table
  • Data Table
  • Stat
  • Timeline
  • Avatar
  • Status Bar
  • Badge
  • Meter
  • Empty
  • Skeleton
  • Spinner
  • Toast
  • Dialog
  • Sheet
  • Popover
  • Tooltip
  • Text
  • Kbd
  • Code Block
  • Utilities
  • Design rules

Table

Rows separated by a hairline, a header that is not a toolbar, and a hover state you can find at any width.

Loading documentation…

Tree< PreviousData TableNext >

Powered by heyo

On this page

UsageCells that carry meaningSortingSelection and actionsSticky headerPropertiesTableTable.RowTable.HeaderTable.CellThe rest
WorkerRegionRequests
acme-apifra1142,318
acme-webwaw18,904
<Table interactive><Table.Head>  <Table.Row>    <Table.Header>Worker</Table.Header>    <Table.Header>Region</Table.Header>    <Table.Header align="end" numeric>Requests</Table.Header>  </Table.Row></Table.Head><Table.Body>  <Table.Row>    <Table.Cell primary>acme-api</Table.Cell>    <Table.Cell>fra1</Table.Cell>    <Table.Cell numeric>142,318</Table.Cell>  </Table.Row>  <Table.Row>    <Table.Cell primary>acme-web</Table.Cell>    <Table.Cell>waw1</Table.Cell>    <Table.Cell numeric>8,904</Table.Cell>  </Table.Row></Table.Body></Table>

Three decisions do most of the work in this component.

  1. Rows are separated by a hairline, not by a fill. Zebra striping is opt-in and deliberately weak, because on a dense table it fights every other tint the system uses for meaning.
  2. The header is not a toolbar. It sits on the page background with uppercase micro-type, so the eye reads it as a label row and then stops coming back to it.
  3. The hover state is a full-bleed tint plus a left marker. On a table 1200px wide a tint alone is invisible under the cursor at the far right; the marker tells you which row you are actually on.

Usage

tsx
import { Table } from "@heyo-sh/heyo-ui";<Table>  <Table.Head>    <Table.Row>      <Table.Header>Worker</Table.Header>    </Table.Row>  </Table.Head>  <Table.Body>    <Table.Row>      <Table.Cell primary>acme-api</Table.Cell>    </Table.Row>  </Table.Body></Table>;
Want search, sorting and selection?

DataTable is this component with all of it wired up. Compose Table by hand when the list is simple, or when the behaviour is yours.

Cells that carry meaning

primary marks the row's headline — the id, the name, the thing you scan for. It gets full contrast and medium weight so everything else can stay quiet. numeric switches to tabular figures and right alignment, which is what makes a column of numbers comparable at a glance.

Sorting

onSort turns a header into a button; sort says which way it is pointing. false means "sortable, but not currently sorted".

acme-api142,318
acme-web8,904
<Table.Header sort="desc" onSort={() => toggle("requests")} align="end">Requests</Table.Header>

Selection and actions

Table.SelectHeader and Table.SelectCell are the narrow first column for checkboxes; useSelection computes the header's indeterminate state. Table.ActionCell is the trailing column: it fades its contents in on hover, so fifty rows are not fifty buttons competing with the data — but keeps them visible whenever the keyboard is inside the cell.

Worker
acme-api
acme-web
<Table.Row selected><Table.SelectCell>  <Checkbox checked aria-label="Select acme-api" /></Table.SelectCell><Table.Cell primary>acme-api</Table.Cell><Table.ActionCell>  <Button variant="ghost" size="sm" shape="square" icon={IconPencil} aria-label="Edit" /></Table.ActionCell></Table.Row>

Sticky header

stickyHeader pins the header while the body scrolls. It needs a bounded maxHeight — there is nothing to scroll inside otherwise.

tsx
<Table stickyHeader maxHeight="24rem" striped>  …</Table>

Properties

Table

stripedbooleanoptional

Tints every other row. Helps when scanning wide, numeric tables.

interactivebooleanoptional

Highlights rows on hover. Only useful when rows are clickable.

stickyHeaderbooleanoptional

Pins the header. Needs a bounded maxHeight.

maxHeightstringoptional

Caps the scroll container, for example "24rem".

wrapperClassNamestringoptional

Class for the scroll container around the <table>.

Table.Row

selectedbooleanoptional

Tints the row and exposes data-selected, so the whole row reflects its checkbox instead of leaving a lone tick to carry the state.

Table.Header

align"start" | "center" | "end"optional

Logical alignment. It shadows the deprecated HTML align attribute on purpose.

onSort() => voidoptional

Makes the header a sort button.

sort"asc" | "desc" | falseoptional

The current sort for this column. false means sortable but not sorted.

Table.Cell

align"start" | "center" | "end"optional

Logical alignment.

numericbooleanoptional

Tabular figures, right-aligned — for anything counted or measured.

primarybooleanoptional

The row's headline. Full contrast, medium weight.

The rest

Table.Head / Body / Caption—optional

The semantic sections: <thead>, <tbody>, <caption>.

Table.SelectHeader / SelectCell—optional

The narrow first column that holds a checkbox.

Table.ActionCell—optional

The trailing column for row actions, revealed on hover or keyboard focus.