Table

A data table with styled header, body, footer, and row hover states. Supports striped rows for easier scanning.

Table is a static primitive: it ships its own horizontally scrolling, rounded, bordered container, so you never have to wrap it yourself. Sorting, row selection wiring and pinned columns live one tier up, in the DataTable composite.

Basic usage

InvoiceCustomerAmountStatus
INV-001Alice Johnson$250.00Paid
INV-002Bob Smith$150.00Pending
INV-003Carol White$350.00Paid
INV-004David Brown$450.00Overdue

Striped variant

Use variant="striped" — or the boolean shorthand striped — for alternating row backgrounds.

InvoiceCustomerAmountStatus
INV-001Alice Johnson$250.00Paid
INV-002Bob Smith$150.00Pending
INV-003Carol White$350.00Paid
INV-004David Brown$450.00Overdue

With footer

InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00
INV-003Carol White$350.00
INV-004David Brown$450.00
Total$1,200.00

Selected rows

Set data-state="selected" on a TableRow to mark it as chosen. Every cell in the row takes the --accent tint — the same fill DataGrid gives a selected row.

InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00
INV-003Carol White$350.00
INV-004David Brown$450.00

The tint is painted on the cells, not the <tr>. A striped table colours even rows from the <table>, and that selector outranks any class on the row, so a row-level fill would vanish on every second row. On the cells it wins, and because --accent is opaque, the sticky pinned cells of DataTable stay solid when the table scrolls sideways. There is no border, so selecting a row shifts nothing.

Density

Row height is not hard-coded. TableHead takes its height from --table-head-h and TableCell its vertical padding from --row-py, so any table below a data-density="compact" subtree tightens from 40px/12px to 32px/6px without a single prop change. Wire the attribute with DensityProvider from @comitor/ui/shell; the scale is vertical only, so columns keep their padding and text never crowds the cell edge.

InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00
INV-003Carol White$350.00
INV-004David Brown$450.00

Caption

TableCaption renders a real <caption>, placed below the table, and names it for screen readers.

Invoices issued in the last 30 days.
InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00

Props

PropTypeDefaultDescription
variant'default' | 'striped''default'Visual style. Striped tints every even body row with the table-row-stripe token.
stripedbooleanfalseConvenience alias for variant="striped". Either one turns striping on.
...propsReact.ComponentProps<'table'>—All native table attributes are forwarded to the inner <table>; className lands there too, not on the scroll container.

Sub-components

Every part is a thin wrapper over its native element and forwards all of that element's props.

PropTypeDefaultDescription
TableHeaderReact.ComponentProps<'thead'>—The <thead>. Carries the muted header fill and the bottom border.
TableBodyReact.ComponentProps<'tbody'>—The <tbody>. Drops the border on the last row so it does not double up with the container edge.
TableFooterReact.ComponentProps<'tfoot'>—The <tfoot>. Muted fill, top border, medium weight — for totals and summary rows.
TableRowReact.ComponentProps<'tr'>—A <tr> with hover tint. Set data-state="selected" to mark it as chosen.
TableHeadReact.ComponentProps<'th'>—A header cell: uppercase, tracked, muted; its height follows the --table-head-h density token.
TableCellReact.ComponentProps<'td'>—A body cell; its vertical padding follows the --row-py density token.
TableCaptionReact.ComponentProps<'caption'>—A caption rendered below the table, in muted text.

Accessibility

  • Uses semantic <table>, <thead>, <tbody>, <tfoot>.
  • Column headers use <th> for proper screen reader announcement.
  • The horizontal scroll container is built in — Table wraps itself in a data-slot="table-container" div with overflow-auto, so wide tables scroll on small screens without an extra wrapper.
  • Give the table a name with TableCaption, or an aria-label on the <table>, whenever a page shows more than one.
  • Pair the selected tint with a checkbox or a text summary of the selection, as DataTable's selectable does, so the state does not rest on colour alone.