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

Source: https://ui.suiss.app/docs/components/table

**React**

**Example**

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

```bash title="Terminal"
npx shadcn@latest add @esuiss/table
```

Works in any project with a `components.json`. See [Installation](https://ui.suiss.app/docs/installation#add-components) for the one-line registry setup. Or use the suiss/ui CLI:

```bash title="Terminal"
npx @esuiss/ui@latest add table
```

### Manual

1. Add the [`cn` helper](https://ui.suiss.app/docs/installation#the-cn-helper).

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

```tsx title="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}
      />
    </div>
  )
}

function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
  return (
    <thead
      data-slot="table-header"
      className={cn("[&_tr]:border-b", className)}
      {...props}
    />
  )
}

function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
  return (
    <tbody
      data-slot="table-body"
      className={cn("[&_tr:last-child]:border-0", className)}
      {...props}
    />
  )
}

function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
  return (
    <tfoot
      data-slot="table-footer"
      className={cn(
        "border-t bg-muted/50 font-semibold [&>tr]:last:border-b-0",
        className
      )}
      {...props}
    />
  )
}

function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
  return (
    <tr
      data-slot="table-row"
      className={cn(
        "border-b transition-colors hover:bg-accent has-aria-expanded:bg-accent data-selected:bg-accent hover:text-accent-foreground",
        className
      )}
      {...props}
    />
  )
}

function TableHead({ className, ...props }: React.ComponentProps<"th">) {
  return (
    <th
      data-slot="table-head"
      className={cn(
        "h-12 px-3 text-start align-middle text-xs font-semibold whitespace-nowrap text-muted-foreground has-[[role=checkbox]]:pe-0",
        className
      )}
      {...props}
    />
  )
}

function TableCell({ className, ...props }: React.ComponentProps<"td">) {
  return (
    <td
      data-slot="table-cell"
      className={cn(
        "p-3 align-middle whitespace-nowrap tabular-nums has-[[role=checkbox]]:pe-0",
        className
      )}
      {...props}
    />
  )
}

function TableCaption({
  className,
  ...props
}: React.ComponentProps<"caption">) {
  return (
    <caption
      data-slot="table-caption"
      className={cn("mt-4 text-sm text-muted-foreground", className)}
      {...props}
    />
  )
}

export {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableFooter,
  TableHead,
  TableHeader,
  TableRow,
}
```

3. Update the import paths to match your project.

## Usage

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

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

**Example**

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

- `Table`
- `TableCaption`
- `TableHeader`
  - `TableRow`
    - `TableHead`
- `TableBody`
  - `TableRow`
    - `TableCell`
- `TableFooter`
  - `TableRow`
    - `TableCell`

### Table

The `table` element, wrapped in a horizontally scrolling container.

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

### TableHeader

The `thead` section for column headings.

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

### TableBody

The `tbody` section for data rows.

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

### TableFooter

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

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

### TableRow

A `tr` row with a bottom divider and hover background.

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

### TableHead

A `th` column heading in small, secondary text.

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

### TableCell

A `td` data cell with tabular numbers.

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

### 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`.

