Skip to content

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/table

Or import from the package: import { Table } from '@acme/react'

Examples

Basic

Invoices issued in September 2026
InvoiceCustomerStatusAmount
INV-1042Northwind TradersPaid$1,250.00
INV-1043Contoso LtdOverdue$860.00
INV-1044FabrikamDraft$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

Table props
PropTypeDefaultDescription
TableHead.scope'col' | 'row''col'Header scope. Use scope="row" for the first cell of each row.

Accessibility

Keyboard
KeyAction
TabFocuses 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