# Table (/docs/table)

A presentational semantic table with two densities, a pinned header, grid lines, a composable caption, and a loading state.

## Install

```bash
npx shadcn@latest add https://4af.selfsimilar.dev/r/table.json
```

## Usage

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

<Table>
  <TableCaption>People</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Role</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {people.map((person) => (
      <TableRow key={person.id}>
        <TableCell>{person.name}</TableCell>
        <TableCell>{person.role}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>
```

## API

**Main exports:** `Table`, `TableCaption`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`,
`TableHead`, `TableCell`

Native `<table>` semantics with the library's row rules, two densities, a pinned header, optional
grid lines, an optional caption, and a loading state.

This table has no controls and no state. For a checkbox column, a selection action row, column
sorting, or a per-row actions menu, install [`data-table`](/docs/data-table), which composes these
parts.

## Props

### Table

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"sm" \| "md"` | `"sm"` | Row density. |
| `bordered` | `boolean` | `false` | Frame around the table and a rule between every pair of columns. Row rules are always drawn. |
| `stickyHeader` | `boolean` | `true` | Pins the header row to the top of the scroll container. |
| `loading` | `boolean` | `false` | Fill the body with placeholder rows and mark the table busy. |
| `loadingRows` | `number` | `5` | How many placeholder rows to draw. |
| `wrapperClassName` | `string` | — | Class for the scroll container. |
| `rowComponent` | `ComponentType` | `TableRow` | The component placeholder rows are built from. `DataTable` sets this; you should not need to. |

The other parts take their native element's props and add nothing of their own.

`sm` is 36px rows, `md` is 44px; both use a 36px header.

## Caption

`TableCaption` must be the first child, as a native `<caption>` must be. The visible caption is
centered below the scrolling table while the native one stays available to assistive technology.

```tsx
<Table>
  <TableCaption>Release history</TableCaption>
  <TableHeader>{/* ... */}</TableHeader>
</Table>
```

## Loading

`loading` replaces the body's rows with placeholders and sets `aria-busy`. The header keeps its
labels. Placeholder rows take their shape from the header you wrote, so give the header the
columns you intend to render.

```tsx
<Table loading={isLoading} loadingRows={8}>
```

## Layout

The table sits in a scroll container carrying the library's thin scrollbar; style it with
`wrapperClassName`. With `bordered`, the frame is drawn on that container.

The header needs a bounded height to pin to:

```tsx
<Table wrapperClassName="max-h-80">
```

Pinned header cells take `bg-background`. Pass another fill through `className` on `TableHead`
when the table sits on a raised surface.

## Rules

Boundaries — under the header, over the footer, and the frame when `bordered` — are 1.5px at the
full `border` token. Separators between rows and columns are 1px at 60% of it.

A vertical rule belongs to the leading edge of the cell that starts the column, which is how a
column declines its own rule: `data-table`'s checkbox and actions columns draw none.

## Composing on top

`useTableContext()` returns `size`, `bordered`, `stickyHeader`, `loading`, `loadingRows` and
`columns`. `useTableSection()` returns `"header" | "body" | "footer"` — a row can't infer this,
since a `<thead>` and a `<tbody>` hold identical `<tr>`s.

With `rowComponent`, these are what `data-table` builds on. Reach for them if you are adding a
column of your own.