Cognition

Components

Table

A lightweight primitive for simple, static, structured data. It renders plain HTML table elements with Cognition styling and no built-in interactivity.

Table and Data Table serve different needs. Table is a layout primitive for straightforward content. Data Table is a full-featured datagrid with sorting, filtering, and pagination.

Preview

InvoiceStatusMethodAmount
INV-1001PaidCard$2,500.00
INV-1002PendingTransfer$1,200.00
INV-1003PaidCard$650.00
INV-1004OverdueTransfer$3,100.00

Rendered with live Cognition tokens. Toggle the theme and it remaps, no dark: classes.

Variants

A list of recent invoices.
InvoiceStatusAmount
INV-1001Paid$2,500.00
INV-1002Pending$1,200.00
INV-1003Paid$650.00
<TableCaption>A list of recent invoices.</TableCaption>
InvoiceStatusAmount
INV-1001Paid$2,500.00
INV-1002Pending$1,200.00
INV-1003Paid$650.00
Total$4,350.00
<TableFooter>
  <TableRow>
    <TableCell colSpan={2}>Total</TableCell>
    <TableCell className="text-right">$4,350.00</TableCell>
  </TableRow>
</TableFooter>
InvoiceStatusMethodAmount
INV-1001PaidCard$2,500.00
INV-1002PendingTransfer$1,200.00
INV-1003PaidCard$650.00
INV-1004OverdueTransfer$3,100.00
<TableBody className="[&_tr:nth-child(even)]:bg-background-subtle">

The default table, a caption, a footer for totals, and striped rows for scanning longer lists.

States

InvoiceAmount
INV-1001$2,500.00
INV-1002$1,200.00

Table is non-interactive by design and has a single resting state. Rows show a subtle hover only to aid scanning, not to imply they are clickable. For selection, sorting, or pagination, use Data Table.

API

Component
Element
Description
Table
<table>
Root, wrapped in a horizontal scroll container.
TableHeader
<thead>
The header section.
TableBody
<tbody>
The body section holding the rows.
TableFooter
<tfoot>
Footer section, for totals or summaries.
TableRow
<tr>
A single row.
TableHead
<th>
A header cell.
TableCell
<td>
A body cell.
TableCaption
<caption>
An accessible caption for the table.

Each component is a thin wrapper over its HTML element and accepts the standard attributes, including colSpan and className.

Don't and Do

Don't

Don't reach for Table when the data needs sorting, filtering, or pagination. Bolting that behavior onto a plain table rebuilds what Data Table already provides, and it tends to drift out of sync. Use Data Table for anything interactive.

Do
<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Invoice</TableHead>
      <TableHead className="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>INV-1001</TableCell>
      <TableCell className="text-right">$2,500.00</TableCell>
    </TableRow>
  </TableBody>
</Table>

Use it for simple, static tabular data where the structure itself communicates the content.

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

export function Invoices({ rows }) {
  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHead>Invoice</TableHead>
          <TableHead>Status</TableHead>
          <TableHead className="text-right">Amount</TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {rows.map((row) => (
          <TableRow key={row.id}>
            <TableCell className="font-medium">{row.id}</TableCell>
            <TableCell>{row.status}</TableCell>
            <TableCell className="text-right">{row.amount}</TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}