Components
Table
Composable tables: a glass panel that scrolls, sticky headers, first column and totals, sortable columns, rows reordered by drag and drop (into groups and under other rows), and virtual rows for long lists.
| Entry | Category | Amount |
|---|---|---|
| Rent Monthly | Housing | −$1,850 |
| Paycheck Monthly | Salary | +$8,450 |
| Weekly shop | Groceries | −$164 |
Import
import { TableContainer, Table, TableHead, TableCell, TableFooter, TableFooterCell, TableSpacerRow, TableSelectCell, TableSelectHead, SelectionBar, TableSwipeRow, TableSwipeAction } from '@rinzai/zen';Examples
Sortable
Click a heading to sort by it, again to reverse, a third time to clear. The header stays put as the rows scroll.
| Month | |||
|---|---|---|---|
| Oct 2026 | 19,188 | 1,800 | 220,812 |
| Nov 2026 | 19,332 | 1,656 | 201,479 |
| Dec 2026 | 19,477 | 1,511 | 182,002 |
| Jan 2027 | 19,623 | 1,365 | 162,379 |
| Feb 2027 | 19,771 | 1,218 | 142,608 |
| Mar 2027 | 19,919 | 1,070 | 122,689 |
| Apr 2027 | 20,068 | 920 | 102,621 |
| May 2027 | 20,219 | 770 | 82,403 |
| Jun 2027 | 20,370 | 618 | 62,032 |
| Jul 2027 | 20,523 | 465 | 41,509 |
| Aug 2027 | 20,677 | 311 | 20,832 |
| Sep 2027 | 20,832 | 156 | 0 |
Reorder
Rows reordered by drag and drop (SortableTree, with asChild on the table and its rows). Drag a row by its grip within its category, into the other one, into an open sub-item (Eating out), or under another row (drag sideways a level in or out); or Tab to a grip and use Space and the arrow keys. Categories stay at the top level and can be reordered too. The first column stays in view as it scrolls sideways.
| Item | Jul | Aug | Sep | Oct |
|---|---|---|---|---|
| Home | ||||
| Rent | $1800 | $1800 | $1800 | $1800 |
| Electricity | $92 | $88 | $75 | $70 |
| Internet | $60 | $60 | $60 | $60 |
| Food | ||||
| Groceries | $420 | $395 | $410 | $430 |
| Eating out | $180 | $150 | $210 | $160 |
| Coffee | $42 | $38 | $45 | $40 |
| Takeaway | $64 | $50 | $80 | $55 |
Selectable
Pick rows (shift-click for a run of them), then act on them all from the bar.
| Entry | Amount | |
|---|---|---|
| Weekly shop | −$165 | |
| Farmers market | −$42 | |
| Train pass | −$92 | |
| Dinner with Sam | −$78 | |
| Birthday lunch | −$92 |
Swipe
On a phone, swipe a row left for its actions; with a keyboard, tab to them.
| Entry | Amount | |
|---|---|---|
| Weekly shop | −$165 | |
| Farmers market | −$42 | |
| Train pass | −$92 | |
| Dinner with Sam | −$78 |
Detail
Open an entry and its row grows into its details (a view transition, with share="zen-morph").
| Entry | Date | Amount |
|---|---|---|
| Oct 4 | −$165.20 | |
| Oct 2 | −$92.00 | |
| Oct 1 | +$4,200.00 |
Virtual
10,000 rows, but only the ones in view (plus a few either side) are rendered: useVirtualList with spacer rows.
| # | Date | Payee | Amount |
|---|---|---|---|
| 1 | Jan 1 | Grocer | $1.50 |
| 2 | Jan 1 | Utilities | $80.69 |
| 3 | Jan 1 | Cinema | $159.88 |
| 4 | Jan 1 | Bookshop | $39.07 |
| 5 | Jan 1 | Pharmacy | $118.26 |
| 6 | Jan 1 | Cafe | $197.45 |
| 7 | Jan 1 | Landlord | $76.64 |
| 8 | Jan 1 | Metro | $155.83 |
| 9 | Jan 1 | Grocer | $35.02 |
| 10 | Jan 1 | Utilities | $114.21 |
| 11 | Jan 1 | Cinema | $193.40 |
| 12 | Jan 1 | Bookshop | $72.59 |
| 13 | Jan 1 | Pharmacy | $151.78 |
| 14 | Jan 1 | Cafe | $30.97 |
| 15 | Jan 1 | Landlord | $110.16 |
| 16 | Jan 1 | Metro | $189.35 |
| 17 | Jan 1 | Grocer | $68.54 |
| 18 | Jan 1 | Utilities | $147.73 |
API reference
<TableContainer>
The panel a table scrolls in: glass with an edge that catches the pointer light, like a Card. Give it a max height (className) to scroll vertically; it scrolls sideways when the table is wider. ref and scroll events are the scrolling element's. With label, it's a focusable region, so keyboard users can scroll it too.
| Prop | Type | Default |
|---|---|---|
labelAccessible name; makes the scroll area a focusable region. | string | — |
<Table>
The <table>. Put it in a TableContainer for the panel and scrolling.
No props of its own; it accepts the props of the element it renders.
<TableHead>
Column heading: small spaced caps (TableHeader keeps the row at the top). With onSort, it's a button that shows the direction and announces it (aria-sort).
| Prop | Type | Default |
|---|---|---|
numericAligned to the end, for numbers. | boolean | — |
onSortMakes the heading a sort button. | (() => void) | — |
sortDirection | SortDirection | — |
stickyStay in view when the table scrolls sideways. | "start" | — |
<TableCell>
A cell: hairline below, lit with its row on hover. sticky="start" keeps it in view when scrolling sideways.
| Prop | Type | Default |
|---|---|---|
numericAligned to the end, tabular digits. | boolean | — |
stickyStay in view when the table scrolls sideways. | "start" | — |
<TableFooter>
Rows that stay at the bottom while the body scrolls (totals, balances).
No props of its own; it accepts the props of the element it renders.
<TableFooterCell>
A footer cell: sits on the card, above a hairline. Use inside TableFooter.
| Prop | Type | Default |
|---|---|---|
numericAligned to the end, tabular digits. | boolean | — |
stickyStay in view when the table scrolls sideways. | "start" | — |
<TableSpacerRow>
An empty row of a given height, standing in for rows scrolled out of view (see useVirtualList).
| Prop | Type | Default |
|---|---|---|
colSpanRequired | number | — |
heightRequired | number | — |
<TableSelectCell>
A row's checkbox cell; spread rowProps(key) from useSelection into it.
| Prop | Type | Default |
|---|---|---|
checkedRequired | boolean | — |
labelRequiredThe checkbox's name, e.g. "Select Rent". | string | — |
onSelectRequiredWith | (on: boolean, range: boolean) => void | — |
<TableSelectHead>
The header's checkbox, for every row at once; spread allProps() from useSelection into it.
| Prop | Type | Default |
|---|---|---|
checkedRequired | boolean | — |
indeterminateRequired | boolean | — |
onSelectRequired | (on: boolean) => void | — |
label | string | — |
<SelectionBar>
What to do with the selected rows: their count, your actions, and a × to clear the selection. Floats at the bottom of the screen while any are selected (move it with className).
| Prop | Type | Default |
|---|---|---|
countRequired | number | — |
onClearRequired | () => void | — |
childrenThe actions, e.g. buttons to delete or re-tag them. | ReactNode | — |
className | string | — |
clearLabel | string | — |
labelThe count as text (default "3 selected"). | ((count: number) => string) | — |
<TableSwipeRow>
A row whose actions sit behind it at its end: swipe it left to reveal them (right, in right-to-left layouts), back (or tap the row) to put them away. They also come out when focused, so the keyboard reaches them. Use it in a TableContainer.
| Prop | Type | Default |
|---|---|---|
actionsRequiredWhat's behind the row: TableSwipeActions, e.g. Edit and Delete. | ReactNode | — |
selected | boolean | — |
<TableSwipeAction>
One of a swipe row's actions: fills the row's height, flush with its end.
| Prop | Type | Default |
|---|---|---|
tone | "default" | "negative" | default |