Table
Responsive semantic table primitives for structured data and rich states
pnpm add @wandercom/design-system-web
Compose the table primitives directly. TableContainer adds horizontal
overflow on small screens while Table stays a plain semantic table that is
easy to use with server rendering or a virtualized data layer.
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableHeader,
TableRow,
} from '@wandercom/design-system-web/ui/table';
export function ReservationsTable({ reservations }) {
return (
<TableContainer>
<Table className="min-w-[720px]">
<TableHeader>
<TableRow className="h-10">
<TableHead>Property</TableHead>
<TableHead>Customer</TableHead>
<TableHead>Date</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{reservations.map((reservation) => (
<TableRow interactive key={reservation.id}>
<TableCell>{reservation.property}</TableCell>
<TableCell>{reservation.customer}</TableCell>
<TableCell>{reservation.dates}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}Table cells accept any content, including image-and-label pairs, badges, and two-line values.
Set density="compact" on Table for full-page data grids. Rows are 40px with
no dividers between them (whitespace, column alignment and the hover tint carry
the rhythm), the header is 32px with small secondary labels (4.5:1 contrast at 12px) and a border-primary rule,
cells pad 12px, and the first and last columns inset 24px to line up with page
gutters. Mark numeric
columns with numeric on TableHead and TableCell to right-align them with
tabular numerals, and set selected on TableRow for rows chosen with a
checkbox. Pair compact tables with TableState size="compact".
A part's className still overrides its density values, for example
className="px-0" on a checkbox column's cells.
Use TableState inside TableBody to preserve the header while centering an
empty, loading, or permissions state across the table columns.
Override contentClassName when a state needs additional vertical room for
actions or a longer message.
Table ships without a data engine, state, effects, or client-only directive.
For ordinary datasets, map rows directly as shown above. For very long
datasets, use these primitives as the styled markup emitted by TanStack Table
and a virtualizer such as react-virtuoso; TableContainer is intentionally
separate so a virtualizer can own its scroll parent.
Keep a readable minimum table width and place it in TableContainer for
horizontal touch scrolling on narrow layouts:
<TableContainer>
<Table className="min-w-[720px]">{/* columns and rows */}</Table>
</TableContainer>