# Simple table (goodtabs UI)

A typed data table with column sorting, a sticky header and an empty state.

## Install

```bash
npx shadcn@latest add https://ui.goodtabs.co/r/simple-table.json
```

Or, with the @goodtabs namespace configured in components.json: `npx shadcn@latest add @goodtabs/simple-table`

## Usage

```tsx
import { SimpleTable, type SimpleTableColumn } from "@/components/ui/simple-table"

type Member = { id: number; name: string; role: string }

const columns: SimpleTableColumn<Member>[] = [
  { key: "name", header: "Name", sortable: true },
  { key: "role", header: "Role" },
]

<SimpleTable columns={columns} rows={members} getRowId={(m) => m.id} />
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| columns | `SimpleTableColumn<T>[]` |  | Column definitions: key, header, align, sortable, sortValue, render, className. |
| rows | `T[]` |  | The data. Sorting never mutates it. |
| getRowId | `(row: T) => string | number` |  | Stable id per row, used as the React key. |
| caption | `string` |  | Visible caption under the table. |
| stickyHeader | `boolean` | `false` | Keeps the header in view. Give the wrapper a height via className. |
| density | `"default" | "dense"` | `"default"` | Row height, 40px or 32px. |
| empty | `ReactNode` | `"Nothing to show."` | Shown instead of the body when rows is empty. |
| defaultSort | `{ key; direction: "asc" | "desc" } | null` | `null` | Initial sort column and direction. |

## When to use

- Lists of records with a handful of columns: members, deploys, invoices, logs
- When readers want to sort by a column without a round trip
- Settings and admin screens where a full data grid would be too much

## When not to use

- More than a few hundred rows: paginate or virtualise on the server instead
- Row selection, inline editing or column resizing: that is a data grid
- Layout. Never use a table to arrange content

## Notes for agents

- getRowId and render are functions, so the component that renders SimpleTable must be a client component ("use client"), or the build fails with a functions-to-client-components error
- columns.key must be a key of the row type; the generic T is inferred from rows
- Sorting compares numbers numerically and strings with localeCompare; pass sortValue when the raw value is not comparable, like a formatted date
- stickyHeader only works when the wrapper has a fixed height and scrolls; pass h-64 or similar in className
- Clicking a sorted header cycles ascending, descending, then unsorted

Docs: https://ui.goodtabs.co/components/simple-table