Skip to content
Redpanda UI
Esc
↑↓navigate↵open⌘Jpreview
Made by shadcn
Edit on GitHub

Table

A responsive table component.

Powered by

Installation

bash
npx shadcn@latest add @redpanda/table
bash
pnpm dlx shadcn@latest add @redpanda/table
bash
yarn dlx shadcn@latest add @redpanda/table
bash
bunx shadcn@latest add @redpanda/table
bash
nubx shadcn@latest add @redpanda/table
bash
aube dlx shadcn@latest add @redpanda/table

When to use

Tables display structured data in rows and columns. Use this decision tree:

Usage

tsx
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
tsx
<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.

tsx
<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:

tsx
<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.

tsx
<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.

tsx
<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

Credits

  • We take our inspiration from Shadcn UI for the table component and style.

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
  • minorv0.3.0Add theme-provider component to the registry with documentation and tests. Includes playground type improvements (export RegistryItem, remove as-const boilerplate) and docs site dark mode border color fix.#109
See full history →

Last updated on

Was this page helpful?