Table
The table.tsx module provides a composable table. TableBody renders its rows normally by default; give the enclosing Table a maxHeight and TableBody automatically windows its rows so thousands of rows can be rendered without DOM overflow — no separate virtualized component required, and no risk of the header and body columns drifting out of alignment, since rows always stay in native table layout.
Interactive example#
Table · Live example
Project | Status | Progress | Action |
|---|---|---|---|
Developer portal 02 | In progress | 26% | |
Developer portal 05 | In progress | 32% | |
Developer portal 08 | In progress | 38% | |
Developer portal 11 | In progress | 44% | |
Developer portal 14 | In progress | 50% | |
Developer portal 17 | In progress | 56% | |
Developer portal 20 | In progress | 62% | |
Developer portal 23 | In progress | 68% | |
Developer portal 26 | In progress | 74% | |
Developer portal 29 | In progress | 80% | |
Developer portal 32 | In progress | 86% |
Filter, sort, select a row, and scroll the automatically windowed table.
The code uses shared demo presentation helpers. Their complete source and styling are included in the LLM build context.
Import#
TSX
import {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
TableHeadRow,
} from '@clidey/ux';
Basic table#
TSX
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Role</TableHead>
<TableHead className="text-right">Joined</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">Jane Doe</TableCell>
<TableCell>jane@example.com</TableCell>
<TableCell><Badge>Admin</Badge></TableCell>
<TableCell className="text-right">Jan 15, 2024</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">John Smith</TableCell>
<TableCell>john@example.com</TableCell>
<TableCell><Badge variant="secondary">Member</Badge></TableCell>
<TableCell className="text-right">Mar 2, 2024</TableCell>
</TableRow>
</TableBody>
</Table>
With caption#
TSX
<Table>
<TableCaption>A list of recent invoices.</TableCaption>
<TableHeader>
{/* ... */}
</TableHeader>
<TableBody>
{/* ... */}
</TableBody>
</Table>
With footer (totals row)#
TSX
<Table>
<TableHeader>
<TableRow>
<TableHead>Item</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Subscription</TableCell>
<TableCell className="text-right">$49.00</TableCell>
</TableRow>
<TableRow>
<TableCell>Add-on: Extra storage</TableCell>
<TableCell className="text-right">$5.00</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell>Total</TableCell>
<TableCell className="text-right font-bold">$54.00</TableCell>
</TableRow>
</TableFooter>
</Table>
Virtualized table (large datasets)#
Pass maxHeight to Table to make it scrollable. TableBody then automatically renders only the rows near the visible area, plus lightweight spacer rows for the rest:
TSX
const COLUMNS = ['ID', 'Name', 'Email', 'Status'];
const rows = generateLargeDataset(10000); // e.g. 10,000 rows
<Table maxHeight={400}>
<TableHeader>
<TableHeadRow>
{COLUMNS.map((col) => (
<TableHead key={col}>{col}</TableHead>
))}
</TableHeadRow>
</TableHeader>
<TableBody rowHeight={48} overscan={5}>
{rows.map((row) => (
<TableRow key={row.id}>
<TableCell>{row.id}</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>{row.email}</TableCell>
<TableCell><Badge>{row.status}</Badge></TableCell>
</TableRow>
))}
</TableBody>
</Table>
rowHeight is only an estimate used to compute which rows are near the viewport before their real height is measured — it accepts a number or a (index: number) => number function for variable-height rows. Actual rendered row heights are measured automatically and used once available, so rowHeight doesn't need to be exact.
Cell with JSON preview#
When a cell contains a large JSON object, TableCell renders an eye icon that opens a formatted JSON preview in a Drawer:
TSX
<TableCell>
{JSON.stringify(largeObject)}
</TableCell>
Overflow detection is automatic — if the content exceeds the cell width, a preview icon appears.
Sub-components#
| Component | Description |
|---|---|
Table | Root <table> element. Accepts maxHeight to make the table scrollable and enable virtualization in TableBody. |
TableHeader | <thead> section. |
TableBody | <tbody> section. Renders all children normally, or virtualizes them when the enclosing Table has maxHeight set. |
TableFooter | <tfoot> section. |
TableRow | <tr> row element. |
TableHead | <th> header cell. |
TableCell | <td> data cell. Has overflow detection. |
TableCaption | <caption> for the table. |
TableProvider | Context provider used internally by Table. |
TableHeadRow | Header row styled to match TableRow. |
Table Props
| Prop | Type | Default | Description |
|---|---|---|---|
maxHeight | number | — | Caps the table's height and makes it scrollable. Required for TableBody to virtualize its rows. |
TableBody Props
| Prop | Type | Default | Description |
|---|---|---|---|
rowHeight | number | ((index: number) => number) | 40 | Estimated height of each row in pixels, used before the real height is measured. Only relevant when the enclosing Table has maxHeight set. |
overscan | number | 3 | Number of rows to render above and below the visible area. Only relevant when virtualized. |
children | ReactNode | — | TableRow elements, same as a plain table body. |
Was this page helpful?
