Table
A responsive table component.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| INV004 | Paid | Credit Card | $450.00 |
| INV005 | Paid | PayPal | $550.00 |
| INV006 | Pending | Bank Transfer | $200.00 |
| INV007 | Unpaid | Credit Card | $300.00 |
| Total | $2,500.00 | ||
<Table> <TableCaption>A list of your recent invoices.</TableCaption> <TableHeader> <TableRow> <TableHead>Invoice</TableHead> <TableHead>Status</TableHead> <TableHead>Method</TableHead> <TableHead sx={styles.end}>Amount</TableHead> </TableRow> </TableHeader> <TableBody> {invoices.map((row) => ( <TableRow key={row.invoice}> <TableCell>{row.invoice}</TableCell> <TableCell>{row.status}</TableCell> <TableCell>{row.method}</TableCell> <TableCell sx={styles.end}>{row.amount}</TableCell> </TableRow> ))} </TableBody></Table>Installation
$ npx shadcn@latest add @constella/tableUsage
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow,} from "@/components/primitives/table";<Table> <TableCaption>A list of your recent invoices.</TableCaption> <TableHeader> <TableRow> <TableHead>Invoice</TableHead> <TableHead>Amount</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>INV001</TableCell> <TableCell>$250.00</TableCell> </TableRow> </TableBody></Table>Composition
Table├── TableCaption├── TableHeader│ └── TableRow│ └── TableHead├── TableBody│ └── TableRow│ └── TableCell└── TableFooter └── TableRow └── TableCellSize
Use the size prop on Table for denser rows, for data a reader scans rather than reads: logs, usage, runs.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| INV004 | Paid | Credit Card | $450.00 |
| Total | $2,500.00 | ||
<Table size="sm">…</Table>| Size | Header height | Cell padding |
|---|---|---|
sm | 32px | 4px |
default | 40px | 8px |
Interactive
Set interactive={false} on Table for a reference table a reader only reads, like a props list: rows stop lighting on hover and press. Selected rows keep their fill.
<Table interactive={false}>…</Table>Footer
Use TableFooter for a totals row.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| Total | $2,500.00 | ||
<TableFooter> <TableRow> <TableCell colSpan={3}>Total</TableCell> <TableCell sx={styles.end}>$2,500.00</TableCell> </TableRow></TableFooter>Actions
Put a Dropdown Menu in the last cell for row actions.
| Product | Price | Actions |
|---|---|---|
| Wireless Mouse | $29.99 | |
| Mechanical Keyboard | $129.99 | |
| USB-C Hub | $49.99 |
<TableCell sx={styles.end}> <DropdownMenu> <DropdownMenuTrigger render={<Button aria-label="Open menu" size="icon" variant="ghost" />} > <HugeiconsIcon icon={MoreHorizontalIcon} /> </DropdownMenuTrigger> <DropdownMenuContent align="end"> <DropdownMenuItem>Edit</DropdownMenuItem> <DropdownMenuItem color="danger">Delete</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu></TableCell>Selected Rows
Add data-state="selected" to a TableRow to mark it. See Checkbox for a table with a checkbox per row.
API Reference
Each part renders its HTML element (Table wraps the <table> in a scrolling container) and takes every prop of it, plus sx, applied last.