Table
A responsive table component.
Powered by
Installation
npx shadcn@latest add @redpanda/tablepnpm dlx shadcn@latest add @redpanda/tableyarn dlx shadcn@latest add @redpanda/tablebunx shadcn@latest add @redpanda/tablenubx shadcn@latest add @redpanda/tableaube dlx shadcn@latest add @redpanda/tableWhen to use
Tables display structured data in rows and columns. Use this decision tree:
Usage
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';<Table> <TableCaption>A list of your recent invoices.</TableCaption> <TableHeader> <TableRow> <TableHead width="sm">Invoice</TableHead> <TableHead>Status</TableHead> <TableHead>Method</TableHead> <TableHead align="right">Amount</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell weight="medium">INV001</TableCell> <TableCell>Paid</TableCell> <TableCell>Credit Card</TableCell> <TableCell align="right">$250.00</TableCell> </TableRow> </TableBody></Table>Use TableCell numeric for numeric values that need equal-width figures. Combine it with
align="right" for a numeric column. numeric does not parse or format values: use
Intl.NumberFormat for currency, units, or locale-aware separators.
Cell appearance
Use component props instead of overriding cell typography, color, or padding:
| Prop | Values | Default behavior |
|---|---|---|
density |
comfortable, standard, compact, dense, none |
comfortable retains p-4. Other densities use py-2, py-1.5, py-1, or p-0. |
font |
body, mono |
Inherits the table font. mono uses font-mono. |
size |
sm, md, lg |
Inherits the table size when omitted. |
tone |
default, muted, success, destructive |
Inherits the text color. muted uses the subtle text token. |
weight |
normal, medium, semibold, bold |
normal |
align |
left, center, right |
left |
truncate |
none, truncate |
none. Truncation also sets max-w-0 to constrain the cell. |
Use TableHead variant="compact" for the shorter, uppercase header treatment. Its default header treatment remains unchanged.
<TableHead variant="compact">Source</TableHead><TableCell density="compact" font="mono" size="sm">broker-0</TableCell><TableCell align="right" density="compact" numeric tone="success">42</TableCell>Stripes, pinned rows and detail cells
Use Table variant="striped" for alternating rendered body rows. The table owns the recessed stripe and matching pinned-column wash; hover, expanded and selected states retain their own feedback. Do not paint individual rows from their array index.
TableRow pinned="top" or pinned="bottom" marks a row that the caller has already ordered into that section. It owns the pinned row’s recessed rest treatment; it does not sort data or add sticky positioning. This is distinct from horizontal boundary-column pinning below.
Use TableCell variant="detail" for an expanded row’s full-width detail content. Keep colSpan equal to the actual visible column count and choose the existing density contract for its padding:
<TableRow> <TableCell colSpan={4} density="comfortable" variant="detail"> Topic configuration </TableCell></TableRow>Pinned boundary columns
Use pinned="start" or pinned="end" on matching TableHead and TableCell elements. These are logical edges, so they follow the table’s writing direction. Pin at most one boundary column per edge: this API does not calculate offsets for multiple pinned columns.
<Table className="min-w-200"> <TableHeader> <TableRow> <TableHead pinned="start">Cluster</TableHead> <TableHead>Region</TableHead> <TableHead align="right" pinned="end"> Monthly cost </TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell pinned="start" weight="medium"> prod-east </TableCell> <TableCell>us-east-1</TableCell> <TableCell align="right" numeric pinned="end"> 12,400 </TableCell> </TableRow> </TableBody></Table>The component owns sticky positioning, the boundary separator, and an opaque card surface so scrolled content cannot show through. Pinned body cells also reflect the row’s hover, expanded, and data-state="selected" washes; matching headers sit above body cells. Pinned cells retain that opaque card surface even with Table variant="simple"; they are not transparent overlays. Horizontal pinning does not make the header vertically sticky.
Sticky header and keyboard scrolling
Set stickyHeader on Table inside a parent with an explicit height. The component fills that parent and owns the single horizontal and vertical scroll viewport, opaque header surface, and header stacking. Do not put a second scrolling wrapper around it or style TableHeader as sticky yourself.
<div className="h-90"> <Table aria-label="Broker metrics" stickyHeader> <TableHeader>{/* Column headings */}</TableHeader> <TableBody>{/* Rows */}</TableBody> </Table></div>onViewportScroll receives React.UIEvent<HTMLElement> from the actual viewport. Read event.currentTarget.scrollTop, scrollHeight, and clientHeight there for windowed rendering or incremental loading. Native onScroll, ref, className, and other table attributes still belong to the underlying <table>; they are not moved to the viewport.
The viewport is a keyboard-focusable named region. Supply the table’s aria-label or aria-labelledby for a meaningful shared name; otherwise the region is called “Scrollable table.” Keyboard users can focus it and use native scrolling keys. A focused activatable TableRow owns its inset three-pixel focus ring; the primitive does not make ordinary rows interactive or add tab stops to them.
stickyHeader is opt-in and does not change unbounded tables. It can be combined with a single pinned boundary column per edge; the header remains above pinned body cells.
Data Table
You can use the <Table /> component to build more complex data tables. Combine it with the installed TanStack Table v9 adapter to create tables with sorting, filtering and pagination.
See the Data Table documentation for more information.
Dropzone Table
You can combine the <Table /> component with file upload functionality to create a table-based file upload interface. This example shows how to display uploaded files in a table format with file icons, progress indicators, and action buttons.
Variants
Striped rows
Bordered
Compact
Caption
Footer totals
Credits
- We take our inspiration from Shadcn UI for the table component and style.