Table
Dumb, styled semantic table on a bordered surface. Pure Server Component with namespaced parts — the primitive DataTable renders through.
Table is a thin, styled wrapper over a semantic <table>, sitting inside a bordered, rounded surface. It's a pure Server Component — no state, no "use client" — so it drops straight into an RSC tree or an MDX page.
You compose it from namespaced parts: an optional non-table header (Table.Toolbar + Table.Title for a title and actions), a Table.Content scroll region that holds the <table>, and the semantic parts Table.Head / Table.Body / Table.Foot / Table.Row / Table.HeaderCell / Table.Cell. The rounded border clips its content, so the corners stay intact even when the scroll region shows a scrollbar.
It carries no variant×color axis — a table is structural. The only knobs are size (density), striped, and stickyHeader / stickyFooter. For a data-driven, sortable/filterable table, reach for DataTable, which renders through this primitive.
import { Table } from '@42/ui-react/table';
<Table>
<Table.Toolbar>
<Table.Title>Members</Table.Title>
<Button size="sm">Add member</Button>
</Table.Toolbar>
<Table.Content>
<Table.Head>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Role</Table.HeaderCell>
<Table.HeaderCell className="text-right">Commits</Table.HeaderCell>
</Table.Row>
</Table.Head>
<Table.Body>
<Table.Row>
<Table.Cell>Ada Lovelace</Table.Cell>
<Table.Cell>Staff</Table.Cell>
<Table.Cell className="text-right">128</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Content>
</Table>Basic
The table parts go inside Table.Content. Numeric columns read best right-aligned — pass className="text-right" to the header and cells (there's no align prop on the primitive; DataTable adds per-column alignment).
| Name | Role | Commits |
|---|---|---|
| Ada Lovelace | Staff | 128 |
| Alan Turing | Student | 57 |
| Grace Hopper | Staff | 203 |
Code
<Table> <Table.Content> <Table.Head> <Table.Row> <Table.HeaderCell>Name</Table.HeaderCell> <Table.HeaderCell>Role</Table.HeaderCell> <Table.HeaderCell className="text-right">Commits</Table.HeaderCell> </Table.Row> </Table.Head> <Table.Body> <Table.Row> <Table.Cell>Ada Lovelace</Table.Cell> <Table.Cell>Staff</Table.Cell> <Table.Cell className="text-right">128</Table.Cell> </Table.Row> </Table.Body> </Table.Content></Table>Header with title & actions
Table.Toolbar is a non-<table> header bar above the scroll region — a title on the left (Table.Title) and any actions on the right. It's separated from the table by a border and stays put when the body scrolls.
Members
| Name | Role |
|---|---|
| Ada Lovelace | Staff |
| Alan Turing | Student |
Code
<Table> <Table.Toolbar> <Table.Title>Members</Table.Title> <Button size="sm">Add member</Button> </Table.Toolbar> <Table.Content>{/* … */}</Table.Content></Table>Striped
striped tints even body rows — easier to scan across wide rows. It targets tbody rows only, so the header is untouched.
| Name | Role |
|---|---|
| Ada Lovelace | Staff |
| Alan Turing | Student |
| Grace Hopper | Staff |
| Katherine Johnson | Staff |
Code
<Table striped> <Table.Content>{/* … */}</Table.Content></Table>Density
size scales cell padding and text from xs to xl (default md), reusing the kit's canonical Size scale.
| Compact (sm) | Role |
|---|---|
| Ada Lovelace | Staff |
| Alan Turing | Student |
| Roomy (lg) | Role |
|---|---|
| Ada Lovelace | Staff |
| Alan Turing | Student |
Code
<Table size="sm">{/* … */}</Table><Table size="lg">{/* … */}</Table>Sticky header & footer
stickyHeader pins the header to the top while the body scrolls; stickyFooter pins a Table.Foot row (a totals/summary row) to the bottom. Both need a bounded height on the scroll region — set max-h-* on Table (bounds the whole surface, toolbar included) or on Table.Content (bounds just the scroll region). The separators are cell borders (border-collapse: separate), so they stay visible while the header/footer are pinned, and the rounded border keeps its corners around the scrollbar.
| # | Name | Commits |
|---|---|---|
| 1 | Contributor 1 | 7 |
| 2 | Contributor 2 | 14 |
| 3 | Contributor 3 | 21 |
| 4 | Contributor 4 | 28 |
| 5 | Contributor 5 | 35 |
| 6 | Contributor 6 | 42 |
| 7 | Contributor 7 | 49 |
| 8 | Contributor 8 | 56 |
| 9 | Contributor 9 | 63 |
| 10 | Contributor 10 | 70 |
| 11 | Contributor 11 | 77 |
| 12 | Contributor 12 | 84 |
| 13 | Contributor 13 | 91 |
| 14 | Contributor 14 | 98 |
| 15 | Contributor 15 | 105 |
| 16 | Contributor 16 | 112 |
| 17 | Contributor 17 | 119 |
| 18 | Contributor 18 | 126 |
| 19 | Contributor 19 | 133 |
| 20 | Contributor 20 | 140 |
| Total | 1470 |
Code
<Table stickyHeader stickyFooter striped className="max-h-64"> <Table.Content> <Table.Head>{/* … */}</Table.Head> <Table.Body>{/* many rows */}</Table.Body> <Table.Foot>{/* pinned totals row */}</Table.Foot> </Table.Content></Table>Horizontal + vertical scroll
Table.Content scrolls on both axes at once. A wide table (more columns than fit) scrolls
horizontally, while a bounded height (max-h-*) plus stickyHeader scrolls it vertically with
the header pinned — the header scrolls sideways in lockstep with the body so columns stay
aligned. Both scrollbars are clipped by the rounded container, so its corners stay clean.
Keep cell content on one line with whitespace-nowrap so columns don't wrap instead of
overflowing.
A soft shadow appears on each horizontal side that still has content to scroll to, and fades
out as you reach that edge — a scroll affordance driven purely by scroll position in CSS (no
JS, so Table stays a Server Component). Browsers without scroll-driven animations simply
show no fade.
| # | Name | Role | Team | Location | Commits | PRs | Reviews | Last active | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Contributor 1 | user1@42.tech | Staff | Order | Paris, FR | 7 | 2 | 3 | 2024-01-15 |
| 2 | Contributor 2 | user2@42.tech | Student | Aliance | Paris, FR | 14 | 4 | 6 | 2024-02-15 |
| 3 | Contributor 3 | user3@42.tech | Student | Federation | Paris, FR | 21 | 6 | 9 | 2024-03-15 |
| 4 | Contributor 4 | user4@42.tech | Staff | Assembly | Paris, FR | 28 | 8 | 12 | 2024-04-15 |
| 5 | Contributor 5 | user5@42.tech | Student | Order | Paris, FR | 35 | 10 | 15 | 2024-05-15 |
| 6 | Contributor 6 | user6@42.tech | Student | Aliance | Paris, FR | 42 | 12 | 18 | 2024-06-15 |
| 7 | Contributor 7 | user7@42.tech | Staff | Federation | Paris, FR | 49 | 14 | 21 | 2024-07-15 |
| 8 | Contributor 8 | user8@42.tech | Student | Assembly | Paris, FR | 56 | 16 | 24 | 2024-08-15 |
| 9 | Contributor 9 | user9@42.tech | Student | Order | Paris, FR | 63 | 18 | 27 | 2024-09-15 |
| 10 | Contributor 10 | user10@42.tech | Staff | Aliance | Paris, FR | 70 | 20 | 30 | 2024-10-15 |
| 11 | Contributor 11 | user11@42.tech | Student | Federation | Paris, FR | 77 | 22 | 33 | 2024-11-15 |
| 12 | Contributor 12 | user12@42.tech | Student | Assembly | Paris, FR | 84 | 24 | 36 | 2024-12-15 |
| 13 | Contributor 13 | user13@42.tech | Staff | Order | Paris, FR | 91 | 26 | 39 | 2024-01-15 |
| 14 | Contributor 14 | user14@42.tech | Student | Aliance | Paris, FR | 98 | 28 | 42 | 2024-02-15 |
| 15 | Contributor 15 | user15@42.tech | Student | Federation | Paris, FR | 105 | 30 | 45 | 2024-03-15 |
| 16 | Contributor 16 | user16@42.tech | Staff | Assembly | Paris, FR | 112 | 32 | 48 | 2024-04-15 |
| 17 | Contributor 17 | user17@42.tech | Student | Order | Paris, FR | 119 | 34 | 51 | 2024-05-15 |
| 18 | Contributor 18 | user18@42.tech | Student | Aliance | Paris, FR | 126 | 36 | 54 | 2024-06-15 |
Code
<Table stickyHeader className="max-h-72"> <Table.Content classNames={{ table: 'min-w-[48rem]' }}> <Table.Head> <Table.Row> <Table.HeaderCell>#</Table.HeaderCell> <Table.HeaderCell>Name</Table.HeaderCell> <Table.HeaderCell>Email</Table.HeaderCell> {/* …more columns… */} </Table.Row> </Table.Head> <Table.Body>{/* many wide rows */}</Table.Body> </Table.Content></Table>Summary footer
Table.Foot renders a <tfoot> with a top separator — a natural home for totals.
| Name | Commits |
|---|---|
| Ada Lovelace | 128 |
| Alan Turing | 57 |
| Grace Hopper | 203 |
| Total | 388 |
Code
<Table> <Table.Content> <Table.Head>{/* … */}</Table.Head> <Table.Body>{/* … */}</Table.Body> <Table.Foot> <Table.Row> <Table.Cell>Total</Table.Cell> <Table.Cell className="text-right">388</Table.Cell> </Table.Row> </Table.Foot> </Table.Content></Table>Subcomponents
Prop
Type
API
Prop
Type
Skeleton
Loading placeholder — a fixed rectangular/circular shape, or a wrap mode that sizes itself to real children and reveals them once loaded.
DataTable
Smart, data-driven table on TanStack Table — sorting, filtering, pagination, selection, and a responsive stacked-card mode, rendered through the Table primitive.