Table
Data table composed from the real table elements, so semantics, keyboard navigation and screen-reader support come from the browser.
| Deploy | Branch | Status | When |
|---|---|---|---|
| 4f2a1c | main | Ready | 2 hours ago |
| 9b7e30 | fix/login | Building | 5 hours ago |
| 1d8c55 | main | Failed | yesterday |
Parts
| Component | Element | Description |
|---|---|---|
Table | table | Wraps itself in a horizontal scroll container. |
TableHeader | thead | Header group, with a bottom border. |
TableBody | tbody | Body group, rows divided by borders. |
TableRow | tr | Row, with a hover tint. |
TableHead | th | Header cell. |
TableCell | td | Body cell, muted by default. |
TableCaption | caption | Description below the table. |
Every part accepts className and forwards all native attributes.
Common patterns
<Table>
<TableHeader>
<TableRow>
<TableHead>Deploy</TableHead>
<TableHead className="text-right">When</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{deploys.map((d) => (
<TableRow key={d.id}>
<TableCell>{d.id}</TableCell>
<TableCell className="text-right">{d.when}</TableCell>
</TableRow>
))}
</TableBody>
</Table>Notes
Table renders its own overflow-x-auto wrapper. A table is the one piece of
content that genuinely cannot reflow on a narrow screen — the alternative is
cells squeezed to two characters wide, which is worse than scrolling.
Right-align numeric columns with className="text-right" on both the head and
the cells, and consider tabular-nums so the digits line up.
There is no sorting, filtering, or pagination here. Those are application concerns with real design decisions behind them; a copy-paste table that guessed at them would be wrong more often than right.