Table
Styled wrapper around native HTML table. Skin only - bring TanStack Table for sorting, filtering, and pagination.
Overview
Table is intentionally a skin, not a behavior. Eight pieces - Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption - render as native table, thead, tbody, tfoot, tr, th, td, and caption with consistent padding, borders, and hover. The native semantics stay intact, so screen readers and assistive tech treat the markup as a real data table.
For sorting, filtering, pagination, row selection, or virtualization, compose Table with @tanstack/react-table (the DataTable pattern in the Patterns layer). The primitive itself never tries to be a data grid - that lock-in lives at the pattern layer.
Preview
| Item | Category | Deps |
|---|---|---|
| rx-message | AI | 4 |
| rx-button | Forms | 1 |
| rx-card | Display | 0 |
| rx-area-chart | Data | 2 |
| rx-alert | Feedback | 1 |
Installation
bash npx gremorie@latest add rx-table bash pnpm dlx gremorie@latest add rx-table bash yarn dlx gremorie@latest add rx-table bash bunx --bun gremorie@latest add rx-table Usage
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
} from "@gremorie/rx-display";
export function Example() {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Item</TableHead>
<TableHead>Category</TableHead>
<TableHead className="text-right">Deps</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-mono">rx-card</TableCell>
<TableCell>Display</TableCell>
<TableCell className="text-right">0</TableCell>
</TableRow>
</TableBody>
</Table>
);
}Angular edition planned for Phase 5h. Star the repo to track progress.
API
<Table>
Wraps the native <table> in a div.relative.w-full.overflow-x-auto container for horizontal scroll on narrow viewports. The table itself is w-full caption-bottom text-sm.
All <table> props are forwarded.
<TableHeader>
Renders as <thead>. Adds [&_tr]:border-b so the row underneath the header always shows the divider.
<TableBody>
Renders as <tbody>. Strips the trailing border on the last row via [&_tr:last-child]:border-0.
<TableFooter>
Renders as <tfoot>. Styled with bg-muted/50 border-t font-medium for sum rows or summary lines.
<TableRow>
Renders as <tr> with hover:bg-muted/50 (interactive feel without claiming interactivity), data-[state=selected]:bg-muted (works with TanStack Table's selection state), and border-b transition-colors.
<TableHead>
Renders as <th>. Defaults: h-10 px-2 text-left align-middle font-medium whitespace-nowrap. Pairs with [role=checkbox] for selection columns.
<TableCell>
Renders as <td>. Defaults: p-2 align-middle whitespace-nowrap. Same checkbox accommodations as TableHead.
<TableCaption>
Renders as <caption>. Defaults: text-muted-foreground mt-4 text-sm. The parent table is caption-bottom, so the caption appears below the table by default.
Composition
<Table>wraps everything. Always include this - the overflow container is what keeps narrow viewports from breaking.<TableCaption>(optional) describes the table content. Place as the first child ofTable- native HTML rule, even though it renders visually at the bottom.<TableHeader>holds one<TableRow>of<TableHead>cells.<TableBody>holds the data rows.<TableFooter>(optional) holds totals, sums, or summary rows.
Every cell should be a TableCell or TableHead - mixing in raw <td> or <th> works but skips the consistent padding and border treatment.
Variations
Data table with caption
| Item | Category | Deps |
|---|---|---|
| rx-message | AI | 4 |
| rx-card | Display | 0 |
| rx-button | Forms | 1 |
<Table>
<TableCaption>Registry inventory by category.</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Item</TableHead>
<TableHead>Category</TableHead>
<TableHead className="text-right">Deps</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-mono">rx-message</TableCell>
<TableCell>AI</TableCell>
<TableCell className="text-right">4</TableCell>
</TableRow>
{/* ... */}
</TableBody>
</Table>With footer summary
| Invoice | Status | Amount |
|---|---|---|
| INV-001 | Paid | $250.00 |
| INV-002 | Pending | $180.00 |
| INV-003 | Paid | $420.00 |
| Total | $850.00 | |
<Table>
<TableHeader>
<TableRow>
<TableHead>Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((inv) => (
<TableRow key={inv.id}>
<TableCell>{inv.id}</TableCell>
<TableCell>{inv.status}</TableCell>
<TableCell className="text-right">{inv.amount}</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={2}>Total</TableCell>
<TableCell className="text-right">$850.00</TableCell>
</TableRow>
</TableFooter>
</Table>Selected row state
<TableRow data-state={selected ? 'selected' : undefined}>
<TableCell>...</TableCell>
</TableRow>The data-[state=selected]:bg-muted styling picks up automatically. TanStack Table sets this attribute for free when you use its row-selection model.
Accessibility
- Native semantics: every part renders as a real HTML element (
table,thead,tbody,th,td,caption), so screen readers announce row and column relationships correctly. No ARIA needed for static tables. - Captions: use
TableCaptionto summarize the table content in one line ("Registry inventory by category"). Place it as the first child ofTable- native HTML requirement. - Column scope: for tables with row headers, add
scope="row"on the leftmost cell andscope="col"on header cells. Both are native attributes you pass through toTableHead/TableCell. - Selection columns: when a row has a checkbox, the inline
[&:has([role=checkbox])]:pr-0and[&>[role=checkbox]]:translate-y-[2px]styles align the checkbox correctly. Use a realrole="checkbox"or aCheckboxprimitive. - Sortable columns: ARIA sorting (
aria-sort="ascending" \| "descending" \| "none") belongs onTableHead. Wire it up at the DataTable pattern layer. - Sticky headers: native CSS
position: stickyworks on<th>inside<thead>. AddclassName="sticky top-0 bg-background"for scroll-locked headers. - Overflow scroll: the wrapper
div.overflow-x-autoallows horizontal scrolling on narrow viewports - addtabIndex={0}androle="region"on the wrapper if you need keyboard scrolling.
Related
- Card - common host for a Table inside a dashboard tile.
- Pagination - pairs with Table for paginated views.
- Checkbox - drop into selection columns.
- DataTable pattern (Patterns layer) - composes Table with TanStack Table for sorting, filtering, pagination, and selection.