suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
GitHubGet started
DocsComponentsPatternsInteractionsChartsBlocks
Components82
  • Overview
  • Accordion
  • Alert
  • Alert Dialog
  • Alert Sheet
  • Amount Input
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Bottom Bar
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Chip
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • FAB
  • Field
  • Fullscreen Menu
  • Haptics
  • Header
  • Hover Card
  • Icon
  • Input
  • Input Accessory Bar
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Keyboard Scroll View
  • Keypad
  • Label
  • Marker
  • Menubar
  • Message
  • Message Scroller
  • Native Select
  • Navigation Menu
  • Page Indicator
  • Pagination
  • Pin Pad
  • Popover
  • Progress
  • QR Scanner
  • Questionnaire
  • Radio Group
  • Resizable
  • Screen
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Signature Pad
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Tab Bar
  • Table
  • Tabs
  • Text
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip
  • Wheel Picker
  • Widget

suiss/ui · Component

Table

Styled native table elements for displaying rows of structured data.

Table is an open-source component from suiss/ui for React. The @esuiss/ui CLI copies its source into your project, so you own the code.

import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableFooter,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"

const invoices = [
  { id: "INV-001", status: "Paid", method: "Credit card", amount: 250 },
  { id: "INV-002", status: "Pending", method: "PayPal", amount: 150 },
  { id: "INV-003", status: "Unpaid", method: "Bank transfer", amount: 350 },
  { id: "INV-004", status: "Paid", method: "Credit card", amount: 450 },
  { id: "INV-005", status: "Paid", method: "PayPal", amount: 550 },
]

const currency = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
})

const total = invoices.reduce((sum, invoice) => sum + invoice.amount, 0)

export default function TableDemo() {
  return (
    <div className="w-[36rem]">
      <Table>
        <TableCaption>A list of your recent invoices.</TableCaption>
        <TableHeader>
          <TableRow>
            <TableHead>Invoice</TableHead>
            <TableHead>Status</TableHead>
            <TableHead>Method</TableHead>
            <TableHead className="text-end">Amount</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          {invoices.map((invoice) => (
            <TableRow key={invoice.id}>
              <TableCell className="font-medium">{invoice.id}</TableCell>
              <TableCell>{invoice.status}</TableCell>
              <TableCell>{invoice.method}</TableCell>
              <TableCell className="text-end">
                {currency.format(invoice.amount)}
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
        <TableFooter>
          <TableRow>
            <TableCell colSpan={3}>Total</TableCell>
            <TableCell className="text-end">{currency.format(total)}</TableCell>
          </TableRow>
        </TableFooter>
      </Table>
    </div>
  )
}

Table renders a native table inside a container that scrolls horizontally when the table is too wide. Build it from TableHeader, TableBody and TableFooter, each holding TableRow elements with TableHead or TableCell cells. TableCaption adds a caption below the table.

The parts add spacing, hairline row dividers, hover and selection backgrounds, and tabular numbers in cells. They don't add sorting, pagination or selection logic; handle those in your own code.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/table

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add table

Manual

  1. Add the cn helper.

  2. Copy the source into components/ui/table.tsx:

components/ui/table.tsx
"use client"

import type * as React from "react"
import { cn } from "@/lib/utils"

function Table({ className, ...props }: React.ComponentProps<"table">) {
  return (
    <div
      data-slot="table-container"
      className="relative w-full overflow-x-auto"
    >
      <table
        data-slot="table"
        className={cn("w-full caption-bottom text-sm", className)}
        {...props}
      />
  1. Update the import paths to match your project.

Usage

import { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption } from "@/components/ui/table"
<div className="w-[36rem]">
  <Table>
    <TableCaption>A list of your recent invoices.</TableCaption>
    <TableHeader>
      <TableRow>
        <TableHead>Invoice</TableHead>
        <TableHead>Status</TableHead>
        <TableHead>Method</TableHead>
        <TableHead className="text-end">Amount</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      {invoices.map((invoice) => (
        <TableRow key={invoice.id}>
          <TableCell className="font-medium">{invoice.id}</TableCell>
          <TableCell>{invoice.status}</TableCell>
          <TableCell>{invoice.method}</TableCell>
          <TableCell className="text-end">
            {currency.format(invoice.amount)}
          </TableCell>
        </TableRow>
      ))}
    </TableBody>
    <TableFooter>
      <TableRow>
        <TableCell colSpan={3}>Total</TableCell>
        <TableCell className="text-end">{currency.format(total)}</TableCell>
      </TableRow>
    </TableFooter>
  </Table>
</div>

Every part passes its props to the native element, so use standard attributes such as colSpan. Align numeric columns with text-end on both the TableHead and its cells. To highlight a row, set data-selected on TableRow.

Examples

Selected row

Highlights one row with the data-selected attribute.

import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"

const invoices = [
  { id: "INV-001", status: "Paid", method: "Credit card", amount: 250 },
  { id: "INV-002", status: "Pending", method: "PayPal", amount: 150 },
  { id: "INV-003", status: "Unpaid", method: "Bank transfer", amount: 350 },
  { id: "INV-004", status: "Paid", method: "Credit card", amount: 450 },
  { id: "INV-005", status: "Paid", method: "PayPal", amount: 550 },
]

const currency = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
})

export default function TableSelectedRow() {
  return (
    <div className="w-[36rem]">
      <Table>
        <TableHeader>
          <TableRow>
            <TableHead>Invoice</TableHead>
            <TableHead>Status</TableHead>
            <TableHead className="text-end">Amount</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          {invoices.slice(0, 3).map((invoice, index) => (
            <TableRow key={invoice.id} data-selected={index === 1}>
              <TableCell className="font-medium">{invoice.id}</TableCell>
              <TableCell>{invoice.status}</TableCell>
              <TableCell className="text-end">
                {currency.format(invoice.amount)}
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </div>
  )
}

API Reference

Anatomy11 parts
<Table>
<TableCaption />
<TableHeader>
<TableRow>
<TableHead />
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell />
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell />
</TableRow>
</TableFooter>
</Table>
01

Table

The table element, wrapped in a horizontally scrolling container.

Also accepts the props of React.ComponentProps<"table">.

02

TableHeader

The thead section for column headings.

Also accepts the props of React.ComponentProps<"thead">.

03

TableBody

The tbody section for data rows.

Also accepts the props of React.ComponentProps<"tbody">.

04

TableFooter

The tfoot section, shaded and bold, for totals or summaries.

Also accepts the props of React.ComponentProps<"tfoot">.

05

TableRow

A tr row with a bottom divider and hover background.

Also accepts the props of React.ComponentProps<"tr">.

06

TableHead

A th column heading in small, secondary text.

Also accepts the props of React.ComponentProps<"th">.

07

TableCell

A td data cell with tabular numbers.

Also accepts the props of React.ComponentProps<"td">.

08

TableCaption

A caption shown below the table that also names it.

Also accepts the props of React.ComponentProps<"caption">.

Accessibility

  • The parts render native table elements, so rows, column headers and cells keep their built-in roles.
  • TableCaption gives the table its accessible name.

Notes

  • data-selected is matched by presence, so leave the attribute off unselected rows rather than setting it to false.
  • A TableRow that contains an element with aria-expanded also gets the selected background.
  • Header and data cells that contain a role="checkbox" drop their end padding.
  • Parts carry data-slot attributes such as table-container, table, table-row and table-cell.
  • Hover and selection use the accent color; the footer uses a tinted muted.
NextTabsPreviousTab Bar

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • Selected row
  4. 04API Reference
    • Table
    • TableHeader
    • TableBody
    • TableFooter
    • TableRow
    • TableHead
    • TableCell
    • TableCaption
  5. 05Accessibility
  6. 06Notes