Shows data compared across rows and columns.

Code
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"

<Table label="Orders">
  <TableCaption>Orders, October 2026</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead>Order</TableHead>
      <TableHead>Customer</TableHead>
      <TableHead className="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {orders.map((order) => (
      <TableRow key={order.id}>
        <TableCell>{order.id}</TableCell>
        <TableCell>{order.customer}</TableCell>
        <TableCell className="text-right font-mono tabular-nums">{order.amount}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>

Examples

Example source
var P = window.PUI, h = React.createElement;
  var rows = [['#4821', 'Maya Chen', 'success', '✓ Paid', '$1,280.00'], ['#4822', 'Leo Park', 'warning', '! Pending', '$349.90'], ['#4823', 'Sara Diaz', 'destructive', '× Declined', '$2,015.50']];
  ReactDOM.createRoot(document.getElementById('preview')).render(h(P.Table, null,
    h(P.TableCaption, null, 'Orders, October 2026'),
    h(P.TableHeader, null, h(P.TableRow, null, h(P.TableHead, null, 'Order'), h(P.TableHead, null, 'Customer'), h(P.TableHead, null, 'Status'), h(P.TableHead, { className: 'num' }, 'Amount'))),
    h(P.TableBody, null, rows.map(function (r) { return h(P.TableRow, { key: r[0] },
      h(P.TableCell, { style: { fontWeight: 500 } }, r[0]), h(P.TableCell, null, r[1]), h(P.TableCell, null, h(P.Badge, { variant: r[2] }, r[3])), h(P.TableCell, { className: 'num' }, r[4])); }))));

Usage

What you provide

  • TableHeader with short TableHead labels; TableBody with rows; optional TableCaption.
  • Numbers and amounts: right-aligned, font-mono tabular-nums, en-US format ($1,280.00, Oct 21, 2026).
  • Status as a Badge.
  • Empty state: one row with colSpan and a sentence saying what will appear and how to add the first item.

Accessibility

  • Use tables for data compared by rows and columns, never for layout.
  • Always give it a TableCaption (or a heading) that explains what the data is. Column headers are <th scope="col">; for row headers use <th scope="row">.
  • Static cells take no focus. When the table scrolls sideways, PUI makes the container focusable so arrows can scroll it; pass label to name that region.
  • Sortable columns use a button in the header with the current direction exposed (aria-sort on the th); focus stays on the control after sorting.
  • Row selection shows single, partial and all states; say how many records a bulk action affects before running it.
  • Design loading, empty, no-match and failed states as different messages.

Adapted from Magoo 1.6.0 by Marcelo Sales: Tables (tabelas). Magoo is educational guidance; test the behavior in your implementation.

API

Install with:

npx @aline-aroucha/pui add table badge
<Table>
  <TableCaption />
  <TableHeader>
    <TableRow>
      <TableHead />
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell />
    </TableRow>
  </TableBody>
  <TableFooter />
</Table>

Table

NameType
labelstring
Names the scrolling region for screen readers when the table scrolls sideways.

TableCaption

NameType
childrenReactNode
What the data is. Always include one.

TableHead

NameTypeDefault
scope"col" | "row""col"
Which cells this header describes.

TableRow

NameType
data-state"selected"
Highlights a selected row.