Table

A multi-part component for displaying structured data in rows and columns. Supports sorting, striped rows, sticky headers, and flexible data presentation.

3
Variants
3
Sizes
8
Sub-components
Pure
React

Playground

Preview
D
Status
PAY-7291Successemily@example.comDec 20, 2030$316.00
PAY-7292Successjames@example.comDec 20, 2030$242.00
PAY-7293Pendingsarah@example.comDec 21, 2030$837.00
PAY-7294Successdavid@example.comDec 21, 2030$124.00
PAY-7295Failedolivia@example.comDec 22, 2030$495.00
PAY-7296Successmichael@example.comDec 22, 2030$158.00
PAY-7297Pendingsophie@example.comDec 23, 2030$721.00
PAY-7298Successdaniel@example.comDec 24, 2030$89.00
PAY-7299Failedemma@example.comDec 24, 2030$362.00
PAY-7300Successryan@example.comDec 25, 2030$550.00
Variant
Size
Options
<Table>
  <TableHeader>
    <TableRow>
      <TableHead sortable sortDirection={sortKey === 'id' ? sortDir : null} onSort={() => handleSort('id')}>Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead sortable sortDirection={sortKey === 'email' ? sortDir : null} onSort={() => handleSort('email')}>Email</TableHead>
      <TableHead sortable sortDirection={sortKey === 'date' ? sortDir : null} onSort={() => handleSort('date')}>Date</TableHead>
      <TableHead align="right" sortable sortDirection={sortKey === 'amount' ? sortDir : null} onSort={() => handleSort('amount')}>Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {sortedData.map(row => (
      <TableRow key={row.id} interactive>
        <TableCell className="font-mono font-normal">{row.id}</TableCell>
        <TableCell>
          <Badge
            variant="subtle"
            color={statusMap[row.status].color}
            size="sm"
          >
            {statusMap[row.status].label}
          </Badge>
        </TableCell>
        <TableCell className="text-text-muted">{row.email}</TableCell>
        <TableCell className="text-text-muted whitespace-nowrap">{row.date}</TableCell>
        <TableCell align="right" className="font-mono">${row.amount.toFixed(2)}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>

Variants

Default

NameRoleStatus
BobDesignerAway
AliceEngineerActive
<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Role</TableHead>
      <TableHead>Status</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Bob</TableCell>
      <TableCell>Designer</TableCell>
      <TableCell>
        <Badge variant="subtle" color="warning" size="sm">Away</Badge>
      </TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Alice</TableCell>
      <TableCell>Engineer</TableCell>
      <TableCell>
        <Badge variant="subtle" color="success" size="sm">Active</Badge>
      </TableCell>
    </TableRow>
  </TableBody>
</Table>

Bordered

NameRoleStatus
BobDesignerAway
AliceEngineerActive
<Table variant="bordered">...</Table>

Striped

NameRoleStatus
BobDesignerAway
AliceEngineerActive
CharliePMMeeting
DianaDevOpsActive
<Table variant="striped">...</Table>

Sizes

sm
Cell PY8px
Cell PX12px
Font14px
default
Cell PY12px
Cell PX16px
Font14px
lg
Cell PY16px
Cell PX24px
Font16px

API

Component Structure

Table— Pure React
.Header.Body.Footer.RowProps.HeadProps.CellProps.Caption

Props

Table

size"default"
"sm" | "default" | "lg"

Row density (sm: compact / default: standard / lg: spacious)

variant"default"
"default" | "bordered" | "striped"

Visual style of the table

stickyHeaderfalse
boolean

Fix header on scroll

wrapperClassNameundefined
string

className added to the scroll wrapper (e.g., "max-h-[400px]"). Use with stickyHeader

TableRow

interactivefalse
boolean

Show highlight effect on hover

selectedfalse
boolean

Indicates row selected state (adds data-selected attribute)

TableHead

align"left"
"left" | "center" | "right"

Horizontal text alignment

sortablefalse
boolean

Show sort indicator

sortDirectionnull
"asc" | "desc" | null

Current sort direction

onSortundefined
() => void

Callback on sort click

sortIconBuilt-in SVG
{ asc?: ReactNode, desc?: ReactNode, default?: ReactNode }

Customize sort icons (partial override supported)

TableCell

align"left"
"left" | "center" | "right"

Horizontal text alignment

Customization

sortIcon Customize sort icons freely via the sortIcon prop. Partial overrides are also supported.

Replace with Lucide icons

EmailRole
Bobbob@example.comDesigner
Alicealice@example.comEngineer
Charliecharlie@example.comPM
import { ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react' <TableHead sortable sortIcon={{ asc: <ArrowUp className="icon-xs" />, desc: <ArrowDown className="icon-xs" />, default: <ArrowUpDown className="icon-xs" />, }} > Name </TableHead>

Partial override

Grade
95A+
87B+
72C+
// Override only asc and desc (default keeps built-in icon) <TableHead sortable sortIcon={{ asc: <span>↑</span>, desc: <span>↓</span> }}> Score </TableHead>

Tip: Use icon-xs (14px) for sort icons — the ideal size for table header text.

Anatomy

1
2
3
Name
Status
4
5
Alice Johnson
Active
Bob Smith
Inactive
6
Showing 2 of 2 results
Cell PY12px
Cell PX16px
Head Font12px
1
Table
Root container
2
Header Row
Header row
3
Header Cell
Header cell
4
Body Row
Body row
5
Body Cell
Body cell
6
Footer
Footer row

Best Practices

Do

  • Use clear, descriptive header labels
  • Right-align numeric data
  • Use sticky headers for large data sets
  • Set a caption for accessibility

Don't

  • Don't create tables with too many columns
  • Don't use tables for layout purposes
  • Don't embed sort logic inside the component
  • Don't put overly complex content inside cells

Accessibility

Keyboard

TabMove focus between sort buttons
EnterTrigger sort

ARIA / WCAG

  • Uses semantic HTML (table/thead/tbody/th/td)
  • Sort direction announced via aria-sort
  • Sort icons use aria-hidden="true"
  • Table description supported via caption