Light

Data Table

Powerful table and datagrids built using TanStack Table.

Import

import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@timelycare/helix-ui"
import { Badge } from "@timelycare/helix-ui"
import { Checkbox } from "@timelycare/helix-ui"
import { Button } from "@timelycare/helix-ui"
import { Input } from "@timelycare/helix-ui"

Props

// Built with TanStack Table — configure via column definitions
interface ColumnDef<TData> {
  accessorKey?: string
  header: string | (({ column }) => React.ReactNode)
  cell?: ({ row }) => React.ReactNode
  enableSorting?: boolean
  enableHiding?: boolean
}

Design Tokens

Table Container

PropertyTokenValue
Backgroundbg-input-bgWhite (light) / dark surface (dark)
Borderborder1px solid border color
Border radiusrounded-md8px
Overflowoverflow-x-auto
Min width487px

Header Row (TableHead)

PropertyTokenValue
Heighth-1040px
Borderborder-b1px solid border color
Paddingpx-28px horizontal
Min width85px (text columns)
Text sizetext-sm14px
Text colortext-muted-foregroundMuted gray
Font weightfont-semibold600

Table Cell

PropertyTokenValue
Borderborder-b1px solid border color
Paddingp-28px
Min width85px (text columns)
Text sizetext-sm14px
Text colortext-foregroundDefault foreground
Font weightfont-normal400

Checkbox Column

PropertyTokenValue
Widthw-832px
Paddingpl-312px left
Checkbox sizesize-416px × 16px
Checkbox radius4px

Action Column

PropertyTokenValue
Widthw-1664px
Paddingpl-3 pr-212px left, 8px right
Button sizesize-832px × 32px
Icon sizesize-416px × 16px
Icon colortext-primary#19518b

Sortable Header Button

PropertyTokenValue
Heighth-936px
Paddingpx-4 py-216px horizontal, 8px vertical
Border radiusrounded-md8px
Text colortext-muted-foreground#646565
Backgroundbg-transparenttransparent
IconArrowDownUp16px × 16px

Toolbar

PropertyTokenValue
Paddingpy-416px vertical
Input max-widthmax-w-sm384px
Input heighth-936px

Pagination/Caption

PropertyTokenValue
Paddingpy-416px vertical
Text sizetext-sm14px
Text colortext-muted-foreground#646565
Button heighth-936px
Button variantvariant="outline"
Disabledopacity-5050% opacity

Cell Variants

VariantUse ForImplementation
checkboxRow selection<Checkbox /> in cell
textDefault data displayPlain text content
badgeStatus labels, categories<Badge variant="..."> — use Helix Badge variants
actionRow actions menu<DropdownMenu> with Ellipsis icon trigger

Badge Cell Mapping

Badges in table cells must always use Helix outline primitive variants. Do not use shadcn-style default, secondary, destructive, or outline variants in table contexts.

StatusBadge VariantExample
Active / SuccessoutlineSage<Badge variant="outlineSage">Active</Badge>
Pending / In ProgressoutlineNavy<Badge variant="outlineNavy">Pending</Badge>
Cancelled / ErroroutlineBerry<Badge variant="outlineBerry">Cancelled</Badge>
Inactive / DisabledoutlineNeutral<Badge variant="outlineNeutral">Inactive</Badge>

Do not hand-code badge-like <span> elements — always use the <Badge> component to ensure consistent pill-shape (rounded-full), typography (text-xs font-semibold), and theme-aware colors.

Header Variants

VariantUse ForImplementation
checkboxSelect all rows<Checkbox /> component
textNon-sortable columnsPlain text, text-muted-foreground font-semibold
buttonSortable columns<Button variant="ghost"> with ArrowDownUp icon

States

StateImplementation
Defaultborder-b border-border
Hover (row)hover:bg-accent — uniform across all row types
Selectedbg-accent — same color as hover; selected rows have no additional hover state
Sorted AscHeader shows ArrowUp icon
Sorted DescHeader shows ArrowDown icon
Disabled (pagination)opacity-50 pointer-events-none

Icons

Sort icon: size-4 ml-2 (ArrowDownUp) Action icon: size-4 (MoreVertical — vertical ellipsis, text-primary)

<Button variant="ghost" onClick={() => column.toggleSorting()}>
  Email <ArrowUpDown className="ml-2 size-4" />
</Button>

Common Patterns

Basic Table Structure

<div className="rounded-md border">
  <Table>
    <TableHeader>
      <TableRow>
        <TableHead className="w-[100px]">Status</TableHead>
        <TableHead>Email</TableHead>
        <TableHead className="text-right">Amount</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      <TableRow>
        <TableCell><Badge variant="outlineSage">Active</Badge></TableCell>
        <TableCell>user@example.com</TableCell>
        <TableCell className="text-right">$2,500.00</TableCell>
      </TableRow>
    </TableBody>
  </Table>
</div>

With Selection & Actions

<TableRow>
  <TableCell><Checkbox /></TableCell>
  <TableCell>Success</TableCell>
  <TableCell>user@example.com</TableCell>
  <TableCell>
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="ghost" className="h-8 w-8 p-0">
          <MoreVertical className="h-4 w-4" />
        </Button>
      </DropdownMenuTrigger>
    </DropdownMenu>
  </TableCell>
</TableRow>

Toolbar & Pagination

<div className="flex items-center py-4">
  <Input placeholder="Filter emails..." className="max-w-sm" />
  <DropdownMenu>/* Column visibility */</DropdownMenu>
</div>
<Table>/* ... */</Table>
<div className="flex items-center justify-between py-4">
  <span className="text-sm text-muted-foreground">
    0 of 5 row(s) selected.
  </span>
  <div className="space-x-2">
    <Button variant="outline" size="sm">Previous</Button>
    <Button variant="outline" size="sm">Next</Button>
  </div>
</div>

Accessibility

  • Built on TanStack Table — requires manual a11y implementation
  • Use semantic <table>, <thead>, <th>, <tbody> structure
  • Keyboard: Tab to sortable headers, Enter/Space to sort
  • Screen reader: add aria-sort to sortable column headers
  • Row selection: use aria-selected on selected rows

Gotchas

ProblemSolution
Table overflows containerWrap in <div className="rounded-md border overflow-auto">
Checkbox column too wideAdd className="w-[50px]" to TableHead
Actions not alignedUse text-right on Amount, keep actions as last column
Row not clickableAdd onClick to <TableRow> with cursor-pointer

See Also


Last updated: May 1, 2026