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
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV-1001 | Paid | Card | $2,500.00 |
| INV-1002 | Pending | Transfer | $1,200.00 |
| INV-1003 | Paid | Card | $650.00 |
| INV-1004 | Overdue | Transfer | $3,100.00 |
Rendered with live Cognition tokens. Toggle the theme and it remaps, no dark: classes.
Variants
| Invoice | Status | Amount |
|---|---|---|
| INV-1001 | Paid | $2,500.00 |
| INV-1002 | Pending | $1,200.00 |
| INV-1003 | Paid | $650.00 |
<TableCaption>A list of recent invoices.</TableCaption>| Invoice | Status | Amount |
|---|---|---|
| INV-1001 | Paid | $2,500.00 |
| INV-1002 | Pending | $1,200.00 |
| INV-1003 | Paid | $650.00 |
| Total | $4,350.00 | |
<TableFooter>
<TableRow>
<TableCell colSpan={2}>Total</TableCell>
<TableCell className="text-right">$4,350.00</TableCell>
</TableRow>
</TableFooter>| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV-1001 | Paid | Card | $2,500.00 |
| INV-1002 | Pending | Transfer | $1,200.00 |
| INV-1003 | Paid | Card | $650.00 |
| INV-1004 | Overdue | Transfer | $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
| Invoice | Amount |
|---|---|
| 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
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 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.
<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>
);
}