components / display
Table
A styled native table for rows and columns of data.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV-001 | paid | Credit card | $250.00 |
| INV-002 | pending | Bank transfer | $150.00 |
| INV-003 | failed | Credit card | $350.00 |
table-default.tsx
import { Badge } from "@/components/ui/badge";
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
const invoices = [
{ id: "INV-001", status: "paid", method: "Credit card", amount: "$250.00" },
{ id: "INV-002", status: "pending", method: "Bank transfer", amount: "$150.00" },
{ id: "INV-003", status: "failed", method: "Credit card", amount: "$350.00" },
] as const;
const variant = { paid: "brand", pending: "muted", failed: "destructive" } as const;
export default function TableDefault() {
return (
<Table aria-label="Recent invoices" className="max-w-lg">
<TableCaption>Recent invoices</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead numeric>Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((i) => (
<TableRow key={i.id}>
<TableCell className="font-medium">{i.id}</TableCell>
<TableCell>
<Badge variant={variant[i.status]}>{i.status}</Badge>
</TableCell>
<TableCell>{i.method}</TableCell>
<TableCell numeric>{i.amount}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}Installation
$ npx shadcn@latest add https://cd-ui.vercel.app/r/table.jsonfirst time? set up components.json and the theme first: see installation.
Usage
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Ada</TableCell>
</TableRow>
</TableBody>
</Table>Examples
Sticky header, compact, selected
stickyHeader keeps the header while the rows scroll, density sets the row padding, and data-state="selected" marks a row.
| Order | Items | Total |
|---|---|---|
| ORD-001 | 1 | $112.50 |
| ORD-002 | 8 | $150.00 |
| ORD-003 | 6 | $187.50 |
| ORD-004 | 4 | $225.00 |
| ORD-005 | 2 | $262.50 |
| ORD-006 | 9 | $300.00 |
| ORD-007 | 7 | $337.50 |
| ORD-008 | 5 | $375.00 |
| ORD-009 | 3 | $412.50 |
| ORD-010 | 1 | $450.00 |
| ORD-011 | 8 | $487.50 |
| ORD-012 | 6 | $525.00 |
| ORD-013 | 4 | $562.50 |
| ORD-014 | 2 | $600.00 |
| ORD-015 | 9 | $637.50 |
| ORD-016 | 7 | $675.00 |
| ORD-017 | 5 | $712.50 |
| ORD-018 | 3 | $750.00 |
| ORD-019 | 1 | $787.50 |
| ORD-020 | 8 | $825.00 |
| ORD-021 | 6 | $862.50 |
| ORD-022 | 4 | $900.00 |
| ORD-023 | 2 | $937.50 |
| ORD-024 | 9 | $975.00 |
table-sticky.tsx
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
const rows = Array.from({ length: 24 }, (_, i) => ({
id: `ORD-${String(i + 1).padStart(3, "0")}`,
items: (i * 7) % 9 + 1,
total: ((i + 3) * 37.5).toFixed(2),
}));
export default function TableSticky() {
return (
<Table aria-label="Orders" className="max-w-md" density="compact" stickyHeader>
<TableHeader>
<TableRow>
<TableHead>Order</TableHead>
<TableHead numeric>Items</TableHead>
<TableHead numeric>Total</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((r, i) => (
<TableRow data-state={i === 2 ? "selected" : undefined} key={r.id}>
<TableCell className="font-medium">{r.id}</TableCell>
<TableCell numeric>{r.items}</TableCell>
<TableCell numeric>${r.total}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}API
Table
<table> inside a container that scrolls sideways on small screens. Server component.
| prop | type | default |
|---|---|---|
densityRow padding. | "compact" | "default" | "comfortable" | "default" |
stickyHeaderKeeps the header visible while the rows scroll (the container gets a max height). | boolean | false |
aria-labelNames the scroll container as a region. It is keyboard-focusable either way, so you can scroll it with the arrow keys. | string | - |
classNameExtra classes, merged with | string | - |
TableHeader · TableBody · TableFooter
TableHeader, TableBody, and TableFooter map to <thead>, <tbody>, and <tfoot>.
| prop | type | default |
|---|---|---|
classNameExtra classes, merged with | string | - |
TableRow · TableHead · TableCell · TableCaption
TableRow, TableHead, TableCell, and TableCaption map to <tr>, <th>, <td>, and <caption>.
| prop | type | default |
|---|---|---|
numericOn | boolean | false |
classNameExtra classes, merged with | string | - |
Accessibility
- It keeps native table semantics. Use
TableHead(<th>) for headers andTableCaptionto name the table.