Table
Tabular data with real table semantics. Server-component safe.
- Layout: use CSS grid.
- Sorting, filtering and selection: use Data Table (Wave 3).
Install
Terminal
pnpm dlx shadcn@latest add @acme/tableOr import from the package: import { Table } from '@acme/react'
Examples
Basic
| Invoice | Customer | Status | Amount |
|---|---|---|---|
| INV-1042 | Northwind Traders | Paid | $1,250.00 |
| INV-1043 | Contoso Ltd | Overdue | $860.00 |
| INV-1044 | Fabrikam | Draft | $4,300.00 |
| Total | $6,410.00 | ||
table-basic.tsx
import { Badge } from '@/components/ui/badge'
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
const invoices = [
{ id: 'INV-1042', customer: 'Northwind Traders', status: 'Paid', amount: 1250 },
{ id: 'INV-1043', customer: 'Contoso Ltd', status: 'Overdue', amount: 860 },
{ id: 'INV-1044', customer: 'Fabrikam', status: 'Draft', amount: 4300 },
] as const
const STATUS = { Paid: 'success', Overdue: 'destructive', Draft: 'secondary' } as const
const usd = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })
export default function TableBasic() {
return (
<Table>
<TableCaption>Invoices issued in September 2026</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Invoice</TableHead>
<TableHead>Customer</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((inv) => (
<TableRow key={inv.id}>
<TableHead scope="row" className="font-medium text-foreground">
{inv.id}
</TableHead>
<TableCell>{inv.customer}</TableCell>
<TableCell>
<Badge variant={STATUS[inv.status]}>{inv.status}</Badge>
</TableCell>
<TableCell className="text-right tabular-nums">{usd.format(inv.amount)}</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableHead scope="row" colSpan={3} className="text-foreground">
Total
</TableHead>
<TableCell className="text-right tabular-nums">{usd.format(6410)}</TableCell>
</TableRow>
</TableFooter>
</Table>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| TableHead.scope | 'col' | 'row' | 'col' | Header scope. Use scope="row" for the first cell of each row. |
Accessibility
| Key | Action |
|---|---|
| Tab | Focuses the scroll region; arrow keys scroll it |
- Role
- table
- You provide
- A TableCaption naming the table.
- Row headers (TableHead scope="row") for the identifying column.
- Built in
- Wide tables scroll inside a focusable region instead of the page (WCAG 1.4.10).
- WCAG 2.2 criteria
- 1.3.1, 1.4.10, 2.1.1
Don’t
- Use a table for layout.
- Merge cells unless the structure demands it.
Tokens
- color.border
- color.muted
- color.muted-foreground