Table
Semantic table primitives for tabular data with consistent Reva styling.
| Date | Household | Amount | Status |
|---|---|---|---|
| 11 Mar | McCallaghan Family · Josh | -£4,500.00 | Failed |
| 7 Mar | McCallaghan Family · Gaby | £419.00 | Executed |
| 19 Dec 2025 | McCallaghan Family · Josh | -£213.69 | Failed |
| 7 Mar | McCallaghan Family · Gaby | £7,000.00 | Executed |
| 7 Mar | McCallaghan Family · Gaby | £49.00 | Executed |
<div className="w-full max-w-3xl"> <Table> <TableHeader> <TableRow> <TableHead>Date</TableHead> <TableHead>Household</TableHead> <TableHead className="text-right">Amount</TableHead> <TableHead>Status</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>11 Mar</TableCell> <TableCell>McCallaghan Family · Josh</TableCell> <TableCell className="text-right tabular-nums text-red-500">-£4,500.00</TableCell> <TableCell> <Badge variant="subtle" intent="destructive">Failed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>7 Mar</TableCell> <TableCell>McCallaghan Family · Gaby</TableCell> <TableCell className="text-right tabular-nums text-green-500">£419.00</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Executed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>19 Dec 2025</TableCell> <TableCell>McCallaghan Family · Josh</TableCell> <TableCell className="text-right tabular-nums text-red-500">-£213.69</TableCell> <TableCell> <Badge variant="subtle" intent="destructive">Failed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>7 Mar</TableCell> <TableCell>McCallaghan Family · Gaby</TableCell> <TableCell className="text-right tabular-nums text-green-500">£7,000.00</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Executed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>7 Mar</TableCell> <TableCell>McCallaghan Family · Gaby</TableCell> <TableCell className="text-right tabular-nums text-green-500">£49.00</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Executed</Badge> </TableCell> </TableRow> </TableBody> </Table></div>Usage
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@reva/ui";Composition
Use the following composition to build a Table:
Table
├── TableCaption
├── TableHeader
│ └── TableRow
│ ├── TableHead
│ ├── TableHead
│ ├── TableHead
│ └── TableHead
├── TableBody
│ ├── TableRow
│ │ ├── TableCell
│ │ ├── TableCell
│ │ ├── TableCell
│ │ └── TableCell
│ └── TableRow
│ ├── TableCell
│ ├── TableCell
│ ├── TableCell
│ └── TableCell
└── TableFooterExamples
An Actions example (row menus with a dropdown) will be added after the Dropdown Menu component ships in @reva/ui.
Footer and Caption
Use TableFooter for summary rows or totals, and TableCaption to add descriptive text below the table.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| Total | $850.00 | ||
<div className="w-full max-w-2xl"> <Table> <TableCaption>A list of your recent invoices.</TableCaption> <TableHeader> <TableRow> <TableHead className="w-[100px]">Invoice</TableHead> <TableHead>Status</TableHead> <TableHead>Method</TableHead> <TableHead className="text-right">Amount</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell className="font-medium">INV001</TableCell> <TableCell> <Badge>Paid</Badge> </TableCell> <TableCell>Credit Card</TableCell> <TableCell className="text-right">$250.00</TableCell> </TableRow> <TableRow> <TableCell className="font-medium">INV002</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Pending</Badge> </TableCell> <TableCell>PayPal</TableCell> <TableCell className="text-right">$150.00</TableCell> </TableRow> <TableRow> <TableCell className="font-medium">INV003</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Unpaid</Badge> </TableCell> <TableCell>Bank Transfer</TableCell> <TableCell className="text-right">$350.00</TableCell> </TableRow> </TableBody> <TableFooter> <TableRow> <TableCell colSpan={3}>Total</TableCell> <TableCell className="text-right">$850.00</TableCell> </TableRow> </TableFooter> </Table></div>Within Card
Two patterns for Card-embedded tables:
- Bleed —
<CardContent className="px-0!">+<Table variant="embedded">. The subtle header band spans edge-to-edge and the first column aligns optically with theCardHeadertitle viainset. Use this when the table is the primary content of the Card. The!important modifier is required becauseCardContent'sgroup-data-[size=sm]/card:px-4variant outranks a plainpx-0by CSS specificity onsize="sm"Cards. - Contained —
<CardContent>(default padding) +<Table>(default variant). The table sits inside the CardContent padding with flush first/last columns. Use this when the table sits alongside other content.
| Date | Household | Amount | Status |
|---|---|---|---|
| 11 Mar | McCallaghan Family · Josh | -£4,500.00 | Failed |
| 7 Mar | McCallaghan Family · Gaby | £419.00 | Executed |
| 19 Dec 2025 | McCallaghan Family · Josh | -£213.69 | Failed |
| 7 Mar | McCallaghan Family · Gaby | £7,000.00 | Executed |
| 7 Mar | McCallaghan Family · Gaby | £49.00 | Executed |
<div className="w-full max-w-3xl"> <Card> <CardHeader> <CardTitle>Transactions</CardTitle> </CardHeader> <CardContent className="px-0!"> <Table variant="embedded"> <TableHeader> <TableRow> <TableHead>Date</TableHead> <TableHead>Household</TableHead> <TableHead className="text-right">Amount</TableHead> <TableHead>Status</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>11 Mar</TableCell> <TableCell>McCallaghan Family · Josh</TableCell> <TableCell className="text-right font-medium tabular-nums text-red-500">-£4,500.00</TableCell> <TableCell> <Badge variant="subtle" intent="destructive">Failed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>7 Mar</TableCell> <TableCell>McCallaghan Family · Gaby</TableCell> <TableCell className="text-right font-medium tabular-nums text-green-500">£419.00</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Executed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>19 Dec 2025</TableCell> <TableCell>McCallaghan Family · Josh</TableCell> <TableCell className="text-right font-medium tabular-nums text-red-500">-£213.69</TableCell> <TableCell> <Badge variant="subtle" intent="destructive">Failed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>7 Mar</TableCell> <TableCell>McCallaghan Family · Gaby</TableCell> <TableCell className="text-right font-medium tabular-nums text-green-500">£7,000.00</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Executed</Badge> </TableCell> </TableRow> <TableRow> <TableCell>7 Mar</TableCell> <TableCell>McCallaghan Family · Gaby</TableCell> <TableCell className="text-right font-medium tabular-nums text-green-500">£49.00</TableCell> <TableCell> <Badge variant="subtle" intent="neutral">Executed</Badge> </TableCell> </TableRow> </TableBody> </Table> </CardContent> </Card></div>Variants
Use variant="default" (the default) for standalone, page-level tables and for contained tables that sit inside a CardContent's own padding — flush first/last columns with a border under the header. Use variant="embedded" when the table bleeds edge-to-edge inside a Card (or sits inside an Item) — the subtle header band spans the full Card width and inset keeps the first column aligned with the CardHeader title.
| Product | Category | Price |
|---|---|---|
| Laptop | Electronics | 999.99 |
| Desk Chair | Furniture | 150.00 |
| Product | Category | Price |
|---|---|---|
| Laptop | Electronics | 999.99 |
| Desk Chair | Furniture | 150.00 |
<div className="w-full max-w-3xl space-y-6"> <Card> <CardHeader> <CardTitle>Embedded (bleed)</CardTitle> </CardHeader> <CardContent className="px-0!"> <Table variant="embedded"> <TableHeader> <TableRow> <TableHead>Product</TableHead> <TableHead>Category</TableHead> <TableHead className="text-right">Price</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>Laptop</TableCell> <TableCell>Electronics</TableCell> <TableCell className="text-right tabular-nums">999.99</TableCell> </TableRow> <TableRow> <TableCell>Desk Chair</TableCell> <TableCell>Furniture</TableCell> <TableCell className="text-right tabular-nums">150.00</TableCell> </TableRow> </TableBody> </Table> </CardContent> </Card> <Card> <CardHeader> <CardTitle>Default (contained)</CardTitle> </CardHeader> <CardContent> <Table> <TableHeader> <TableRow> <TableHead>Product</TableHead> <TableHead>Category</TableHead> <TableHead className="text-right">Price</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>Laptop</TableCell> <TableCell>Electronics</TableCell> <TableCell className="text-right tabular-nums">999.99</TableCell> </TableRow> <TableRow> <TableCell>Desk Chair</TableCell> <TableCell>Furniture</TableCell> <TableCell className="text-right tabular-nums">150.00</TableCell> </TableRow> </TableBody> </Table> </CardContent> </Card></div>Inset
The inset prop controls the horizontal padding on the first and last column cells. Let the table bleed edge-to-edge with <CardContent className="px-0!">, then pair inset="default" (24px) with Card size="default" and inset="sm" (16px) with Card size="sm" so header labels and body cells align optically with the Card's own padding.
| Product | Price |
|---|---|
| Laptop | 999.99 |
| Desk Chair | 150.00 |
| Product | Price |
|---|---|
| Laptop | 999.99 |
| Desk Chair | 150.00 |
<div className="grid w-full max-w-5xl gap-6 md:grid-cols-2"> <Card> <CardHeader> <CardTitle>Default Card and table inset</CardTitle> </CardHeader> <CardContent className="px-0!"> <Table variant="embedded" inset="default"> <TableHeader> <TableRow> <TableHead>Product</TableHead> <TableHead className="text-right">Price</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>Laptop</TableCell> <TableCell className="text-right tabular-nums">999.99</TableCell> </TableRow> <TableRow> <TableCell>Desk Chair</TableCell> <TableCell className="text-right tabular-nums">150.00</TableCell> </TableRow> </TableBody> </Table> </CardContent> </Card> <Card size="sm"> <CardHeader> <CardTitle>Small Card and table inset</CardTitle> </CardHeader> <CardContent className="px-0!"> <Table variant="embedded" inset="sm"> <TableHeader> <TableRow> <TableHead>Product</TableHead> <TableHead className="text-right">Price</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>Laptop</TableCell> <TableCell className="text-right tabular-nums">999.99</TableCell> </TableRow> <TableRow> <TableCell>Desk Chair</TableCell> <TableCell className="text-right tabular-nums">150.00</TableCell> </TableRow> </TableBody> </Table> </CardContent> </Card></div>Sticky first column
To freeze a column while the rest scroll horizontally, prefer the DataTable's columnPinning — it wires up TanStack's pinning state and renders the sticky offsets, occluding background, and divider for you. On the raw primitives (for a bespoke or non-DataTable table) it's a manual composition: sticky left-0 z-10 on the first TableHead and every first-column TableCell, an occluding background (an opaque bg-canvas or a backdrop-blur glass panel), and a border-r — Table's scroll container handles the overflow (pair with scrollArea for the branded horizontal scrollbar).
Props
Each part forwards native HTML attributes for its element (for example colSpan on TableCell) plus className. Table wraps the <table> in a horizontal scroll container — native overflow-x-auto by default, or the Reva ScrollArea (branded, hover-revealed horizontal scrollbar) when scrollArea is set.
Table
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "embedded" | "default" | "default" — standalone, flush columns, border under header. "embedded" — subtle header band, inset first/last columns (Card/Item-embedded). |
inset | "none" | "xs" | "sm" | "default" | "lg" | "default" (when variant="embedded"); "none" (when variant="default") | Horizontal padding on first/last column cells. Values map to 0 / 8 / 16 / 24 / 32px respectively. |
scrollArea | boolean | false | Swap the native overflow-x-auto container for the Reva ScrollArea — the branded, hover-revealed horizontal scrollbar — for wide grids that scroll sideways. |
Parts
| Export | Element | Role |
|---|---|---|
Table | table | Root; scroll wrapper is a sibling div in the DOM. |
TableHeader | thead | Column headers. |
TableBody | tbody | Data rows. |
TableFooter | tfoot | Footer rows (e.g. totals). |
TableRow | tr | Row; supports data-state="selected" for selected styling. |
TableHead | th | Header cell. |
TableCell | td | Body or footer cell. |
TableCaption | caption | Accessible table description. |
Sortable filters, pagination, and column APIs belong in the Data Table component (TanStack Table on top of these primitives); they are intentionally out of scope for this page.