Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Table

A responsive table component.

Playground

A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
INV004PaidCredit Card$450.00
Total$1,200.00
—TableCaption
—TableFooter
—data-state="selected"
Usage
Compose semantic parts — Table, TableHeader, TableBody, TableRow, TableHead and TableCell. The root wraps the table in an overflow-x-auto container, so wide tables scroll instead of breaking the layout, and TableCaption renders below the rows.
A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
INV004PaidCredit Card$450.00
import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@workspace/ui/components/table"

<Table>
  <TableCaption>A list of your recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead className="w-24">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead>Method</TableHead>
      <TableHead className="text-end">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {invoices.map((invoice) => (
      <TableRow key={invoice.invoice}>
        <TableCell className="font-medium">{invoice.invoice}</TableCell>
        <TableCell>{invoice.status}</TableCell>
        <TableCell>{invoice.method}</TableCell>
        <TableCell className="text-end">{invoice.amount}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>
Footer
TableFooter closes the table with a bg-muted/50 summary band — use colSpan to stretch the label across the data columns.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
INV004PaidCredit Card$450.00
Total$1,200.00
<Table>
  <TableHeader>
    <TableRow>
      <TableHead className="w-24">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead>Method</TableHead>
      <TableHead className="text-end">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {invoices.map((invoice) => (
      <TableRow key={invoice.invoice}>
        <TableCell className="font-medium">{invoice.invoice}</TableCell>
        <TableCell>{invoice.status}</TableCell>
        <TableCell>{invoice.method}</TableCell>
        <TableCell className="text-end">{invoice.amount}</TableCell>
      </TableRow>
    ))}
  </TableBody>
  <TableFooter>
    <TableRow>
      <TableCell colSpan={3}>Total</TableCell>
      <TableCell className="text-end">$1,200.00</TableCell>
    </TableRow>
  </TableFooter>
</Table>
Selected row
Rows tint to bg-muted/50 on hover and to solid bg-muted when marked with data-state="selected" — the hook selection libraries like TanStack Table attach to.
InvoiceStatusAmount
INV001Paid$250.00
INV002Pending$150.00
INV003Unpaid$350.00
<Table>
  <TableHeader>
    <TableRow>
      <TableHead className="w-24">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead className="text-end">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell className="font-medium">INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell className="text-end">$250.00</TableCell>
    </TableRow>
    <TableRow data-state="selected">
      <TableCell className="font-medium">INV002</TableCell>
      <TableCell>Pending</TableCell>
      <TableCell className="text-end">$150.00</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-medium">INV003</TableCell>
      <TableCell>Unpaid</TableCell>
      <TableCell className="text-end">$350.00</TableCell>
    </TableRow>
  </TableBody>
</Table>
Actions
End each row with a ghost icon button wrapped in a DropdownMenu for per-row actions. Keep the trigger compact with size="icon" and give it an sr-only label.
ProductPriceActions
Wireless Mouse$29.99
Mechanical Keyboard$129.00
USB-C Hub$49.50
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@workspace/ui/components/dropdown-menu"
import { Button } from "@workspace/ui/components/button"
import { DotsThreeIcon } from "@phosphor-icons/react"

<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Product</TableHead>
      <TableHead>Price</TableHead>
      <TableHead className="text-end">Actions</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {products.map((item) => (
      <TableRow key={item.product}>
        <TableCell className="font-medium">{item.product}</TableCell>
        <TableCell>{item.price}</TableCell>
        <TableCell className="text-end">
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant="ghost" size="icon" className="size-8">
                <DotsThreeIcon />
                <span className="sr-only">Open menu</span>
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <DropdownMenuItem>Edit</DropdownMenuItem>
              <DropdownMenuItem>Duplicate</DropdownMenuItem>
              <DropdownMenuSeparator />
              <DropdownMenuItem variant="destructive">
                Delete
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>
RTL
Set dir="rtl" on a wrapper or inherit it from a Direction provider — cells use logical properties (text-start, pe-0), so column alignment mirrors automatically.
قائمة بفواتيرك الأخيرة.
الفاتورةالحالةالمبلغ
INV001مدفوعة$250.00
INV002قيد الانتظار$150.00
<div dir="rtl">
  <Table>
    <TableCaption>قائمة بفواتيرك الأخيرة.</TableCaption>
    <TableHeader>
      <TableRow>
        <TableHead className="w-24">الفاتورة</TableHead>
        <TableHead>الحالة</TableHead>
        <TableHead className="text-end">المبلغ</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      <TableRow>
        <TableCell className="font-medium">INV001</TableCell>
        <TableCell>مدفوعة</TableCell>
        <TableCell className="text-end">$250.00</TableCell>
      </TableRow>
      <TableRow>
        <TableCell className="font-medium">INV002</TableCell>
        <TableCell>قيد الانتظار</TableCell>
        <TableCell className="text-end">$150.00</TableCell>
      </TableRow>
    </TableBody>
  </Table>
</div>
API Reference
Every part renders its native table element and accepts the matching element props — none add props of their own. Only Table adds structure: a div wrapper that provides horizontal scrolling.
PartRenders
Tablediv > table
TableHeaderthead
TableBodytbody
TableFootertfoot
TableRowtr
TableHeadth
TableCelltd
TableCaptioncaption

Tokens used

7 design tokens consumed by the Table component.

  • --muted

    Muted

    bg-muted/50hover:bg-muted/50data-[state=selected]:bg-muted
  • --foreground

    Foreground

    text-foreground
  • --muted-foreground

    Muted Foreground

    text-muted-foreground
  • --border

    Border

    border-bborder-t
  • --text-sm

    Text Small (14px)

    text-sm
  • --font-weight-medium

    Font weight Medium (500)

    font-medium
  • --spacing

    Spacing unit (4px)

    h-10px-2p-2mt-4