Table

Styled HTML table primitives (Table / TableHeader / TableBody / TableRow / TableHead / TableCell / TableCaption / TableFooter). Low-level building blocks — use DataTable for sorting/filtering/pagination.

A list of your recent invoices.
InvoiceStatusMethodAmount
INV001
Paid
Credit Card$250.00
INV002
Pending
PayPal$150.00
INV003
Unpaid
Bank Transfer$350.00
INV004
Paid
Credit Card$450.00
Total$1,200.00

Installation

pnpm
pnpm dlx @hex-core/cli add table

With sortable header

Column header is a ghost Button + chevron — wire onClick to your sort state in the parent. aria-sort exposes direction to screen readers.

tsx
<Table>
  <TableCaption>Click a column header to sort.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead aria-sort="ascending">
        <Button variant="ghost" size="sm" className="-ml-3 h-8 data-[state=open]:bg-accent">
          Invoice
          <svg
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            strokeWidth="2"
            strokeLinecap="round"
            strokeLinejoin="round"
            className="ml-2 h-3.5 w-3.5"
            aria-hidden="true"
          >
            <path d="m18 15-6-6-6 6" />
          </svg>
        </Button>
      </TableHead>
      <TableHead>Status</TableHead>
      <TableHead className="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell className="font-medium">INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell className="text-right">$250.00</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-medium">INV002</TableCell>
      <TableCell>Pending</TableCell>
      <TableCell className="text-right">$1,200.00</TableCell>
    </TableRow>
  </TableBody>
</Table>

With selection column

Leading checkbox column for bulk-action data grids — header checkbox toggles select-all; per-row checkboxes wire to your selection state. Skip the data-table primitive when you only need static rows.

tsx
<Table>
  <TableHeader>
    <TableRow>
      <TableHead className="w-12">
        <Checkbox aria-label="Select all rows" />
      </TableHead>
      <TableHead>Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead className="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow data-state="selected">
      <TableCell>
        <Checkbox checked aria-label="Select INV001" />
      </TableCell>
      <TableCell className="font-medium">INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell className="text-right">$250.00</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>
        <Checkbox aria-label="Select INV002" />
      </TableCell>
      <TableCell className="font-medium">INV002</TableCell>
      <TableCell>Pending</TableCell>
      <TableCell className="text-right">$1,200.00</TableCell>
    </TableRow>
  </TableBody>
</Table>

API Reference

PropTypeDefaultDescription
className
stringAdditional CSS classes on the <table>

AI Guidance

When to use

Use for simple tabular data where you render rows manually: invoice lists, pricing rows, settings tables. Responsive container wraps the <table> to allow horizontal scroll on small screens.

When not to use

Don't use for large datasets that need sorting/filtering/pagination (use DataTable). Don't use for layout (use CSS grid/flex). Don't use for <form> field arrays (use native fields).

Common mistakes

  • Using <div> grids instead of a real <table> for tabular data (breaks a11y)
  • Putting interactive controls in headers without keyboard semantics
  • Missing TableCaption when the table has no other label

Accessibility

Semantic <table> / <thead> / <tbody> is used, so screen readers announce rows/columns. Include a TableCaption or aria-label. Mark column sort buttons with aria-sort.

Related components

Token budget: 450