# Data table

> The slotsmith data table: sorting, selection, pagination, tree rows, virtual rows and row reorder, with every part replaceable by your design system's.

The slotsmith data table is a React table built on TanStack Table, with sorting, selection, pagination, tree rows, virtual rows and row reorder built in. Every part it renders is a slot: keep the fallbacks, or pass your design system's own components.

## Overview

The demo below uses the fallbacks only. Click a header to sort, shift-click a second header to sort by both, tick rows to select them, and change the page size under the table.

```tsx
import { useState } from "react";
import { DataTable, type DataTableColumnDef } from "slotsmith/data-table";
import { people, type Person } from "../shared/people";

const columns: DataTableColumnDef<Person>[] = [
  { accessorKey: "name", header: "Name" },
  { accessorKey: "role", header: "Role" },
  { accessorKey: "team", header: "Team" },
  { accessorKey: "status", header: "Status", enableSorting: false },
];

export default function Overview() {
  const [selection, setSelection] = useState<Person[]>([]);
  return (
    <>
      <DataTable
        data={people}
        columns={columns}
        getRowId={(person) => person.id}
        // Shift-click a second header to sort by both columns.
        enableMultiSort
        enableRowSelection
        selection={selection}
        onSelectionChange={setSelection}
        defaultPagination={{ pageIndex: 0, pageSize: 5 }}
        pageSizeOptions={[5, 10]}
      />
      <p>{selection.length === 0 ? "No rows selected." : `Selected: ${selection.map((person) => person.name).join(", ")}`}</p>
    </>
  );
}
```

Each feature has a guide with its own live demo: [sorting and selection](https://slotsmith.dev/components/data-table/guides/sorting-and-selection/), [pagination](https://slotsmith.dev/components/data-table/guides/pagination/), [server data](https://slotsmith.dev/components/data-table/guides/server-data/) with loading, empty and error states, [tree rows](https://slotsmith.dev/components/data-table/guides/tree-rows/), [virtual rows](https://slotsmith.dev/components/data-table/guides/virtual-rows/) for long lists, [row reorder](https://slotsmith.dev/components/data-table/guides/row-reorder/) by pointer, touch or keyboard, [footers](https://slotsmith.dev/components/data-table/guides/footers/), and [the headless hook](https://slotsmith.dev/components/data-table/guides/headless-hook/) for your own markup.

## Install

```bash
npm i slotsmith @tanstack/react-table
```

`VirtualDataTable` from `slotsmith/virtual` also needs `@tanstack/react-virtual`.

```ts
import "slotsmith/data-table.css";
```

Import the stylesheet once, at your app's entry. `slotsmith/styles.css` holds every component's styles instead.

## Quick start

Pass `data` and `columns`. Columns are TanStack Table column definitions; `getRowId` gives each row a stable id.

```tsx
import { DataTable, type DataTableColumnDef } from "slotsmith/data-table";
import "slotsmith/data-table.css";

type Book = { id: string; title: string; author: string; year: number };

const books: Book[] = [
  { id: "1", title: "Things Fall Apart", author: "Chinua Achebe", year: 1958 },
  { id: "2", title: "Season of Migration to the North", author: "Tayeb Salih", year: 1966 },
  { id: "3", title: "The Left Hand of Darkness", author: "Ursula K. Le Guin", year: 1969 },
  { id: "4", title: "Beloved", author: "Toni Morrison", year: 1987 },
  { id: "5", title: "The Remains of the Day", author: "Kazuo Ishiguro", year: 1989 },
  { id: "6", title: "Wolf Hall", author: "Hilary Mantel", year: 2009 },
];

const columns: DataTableColumnDef<Book>[] = [
  { accessorKey: "title", header: "Title" },
  { accessorKey: "author", header: "Author" },
  { accessorKey: "year", header: "Year" },
];

export default function QuickStart() {
  // Sorting is on for every column; pagination shows 10 rows a page by default.
  return <DataTable data={books} columns={columns} getRowId={(book) => book.id} />;
}
```

## Guides

- [Sorting and selection](https://slotsmith.dev/components/data-table/guides/sorting-and-selection/): Sort the slotsmith data table by one or more columns, and select rows with checkboxes, controlled or uncontrolled, with a rule for rows that cannot be selected.
- [Pagination](https://slotsmith.dev/components/data-table/guides/pagination/): Split the slotsmith data table into pages, control the page and page size, change the page-size options, or hide the controls.
- [Server data](https://slotsmith.dev/components/data-table/guides/server-data/): Page and sort the slotsmith data table on a server with manualPagination, manualSorting and rowCount, and show loading, error and empty states.
- [Tree rows](https://slotsmith.dev/components/data-table/guides/tree-rows/): Show nested rows in the slotsmith data table with getSubRows, and control which rows are expanded.
- [Virtual rows](https://slotsmith.dev/components/data-table/guides/virtual-rows/): Render long lists in the slotsmith data table with the virtual prop, which draws only the rows in view.
- [Row reorder](https://slotsmith.dev/components/data-table/guides/row-reorder/): Let people drag rows of the slotsmith data table into a new order by pointer, touch or keyboard, in flat and tree tables, and store the order.
- [Spreadsheet mode](https://slotsmith.dev/components/data-table/guides/spreadsheet/): Edit the slotsmith data table like a spreadsheet: select cells, type over them, paste from Excel, drag the fill handle and undo.
- [Footers](https://slotsmith.dev/components/data-table/guides/footers/): Add a footer row to the slotsmith data table, such as a total, from each column's footer definition.
- [The headless hook](https://slotsmith.dev/components/data-table/guides/headless-hook/): Render the slotsmith data table in markup of your own with useDataTable: the state, the TanStack Table instance and row reorder, with no UI.

## Reference

The [API page](https://slotsmith.dev/components/data-table/api/) is generated from the library's source, so it lists every slot, prop and label the installed version has.

- [Slots](https://slotsmith.dev/components/data-table/api/#slots): Every part you can replace, the props it receives and its fallback.
- [Props](https://slotsmith.dev/components/data-table/api/#props): Every prop, grouped, with its type and default.
- [Labels & locales](https://slotsmith.dev/components/data-table/api/#labels): Every string it shows or announces, and how to translate them.
- [Styling](https://slotsmith.dev/components/data-table/api/#styling): Its class names and the tokens its stylesheet reads.
- [Adapters](https://slotsmith.dev/components/data-table/adapters/): Ready slot maps for shadcn/ui, MUI, Chakra, Ant Design and Radix Themes.

## Adapters

The table works with any design system. Each slot takes a component, so a library's own table, checkbox, button and select can replace the fallbacks. The [Adapters page](https://slotsmith.dev/components/data-table/adapters/) has ready slot maps for shadcn/ui, MUI, Chakra, Ant Design and Radix Themes, as examples of well-known libraries; the same approach works with your own.

## Accessibility

The table renders a real `<table>`, so screen readers announce rows, columns and headers. What the fallbacks add:

- **Sorting.** A sortable header holds a `<button>`, and its `<th>` carries `aria-sort` (`ascending`, `descending` or `none`). With `enableMultiSort`, shift-click adds a column to the sort. See [sorting and selection](https://slotsmith.dev/components/data-table/guides/sorting-and-selection/).
- **Selection.** Each row and the header have a checkbox named by the `selectRow` and `selectAll` labels; the header checkbox is indeterminate while some rows on the page are selected. See [sorting and selection](https://slotsmith.dev/components/data-table/guides/sorting-and-selection/).
- **Tree rows.** The expand toggle is a `<button>` with `aria-expanded`, named by `expandRow` or `collapseRow`. See [tree rows](https://slotsmith.dev/components/data-table/guides/tree-rows/).
- **Pagination.** The controls sit in a `<nav>` named by the `pagination` label. The previous and next buttons are named by `previousPage` and `nextPage`, and the page counter is an `aria-live="polite"` region, so a page change is read out. The page-size control is the slotsmith Autocomplete, a combobox named by the visible `rowsPerPage` text. See [pagination](https://slotsmith.dev/components/data-table/guides/pagination/).
- **Loading and errors.** While loading, the root has `aria-busy="true"` and the skeleton rows are hidden from assistive technology. The error state has `role="alert"` and a retry button. See [server data](https://slotsmith.dev/components/data-table/guides/server-data/).
- **Row reorder.** Each drag handle is a `<button>` named by `reorderRow`, with `aria-describedby` pointing at hidden instructions (`reorderInstructions`) and `aria-pressed="true"` while it is lifted from the keyboard. A visually hidden live region (`role="status"`, `aria-live="assertive"`) reads each step with the `reorderLifted`, `reorderMoved`, `reorderDropped` and `reorderCancelled` labels, as positions such as "Position 2 of 5". In a tree table, a row with no visible sibling has its handle disabled. See [row reorder](https://slotsmith.dev/components/data-table/guides/row-reorder/).

Moving a row from the keyboard, with focus on its drag handle:

| Keys | Action |
| --- | --- |
| Space, Enter | Lift the row. |
| ↑, ↓ | Move the lifted row up or down one place among its siblings. |
| Space, Enter | Drop the row in its new place. Focus stays on its handle. |
| Esc | Cancel. The row goes back where it was. |
| Tab | Cancel, and move focus on as usual. Leaving the handle any other way cancels too. |

Every label can be translated or replaced; see [Labels & locales](https://slotsmith.dev/components/data-table/api/#labels).

## Limitations

- Rows move between tables at the top level only: a tree's sub-rows move within their own table. See [row reorder](https://slotsmith.dev/components/data-table/guides/row-reorder/).
- A `Row` slot defined inside a component is a new component on every render, so every row mounts again. A drag survives it, but define the slot outside the component; a development warning names it. See [row reorder](https://slotsmith.dev/components/data-table/guides/row-reorder/).

## FAQ

### Does the data table support server-side pagination?

Yes. Set manualPagination, pass the current page as data and the total as rowCount, and fetch the next page in onPaginationChange. Server-side sorting works the same way with manualSorting. See https://slotsmith.dev/components/data-table/guides/server-data/

### Can I use virtual rows and row reorder together?

Yes. Pass virtual with useVirtualizer as use and set enableRowReorder: the dragged row stays rendered while it scrolls out of the window, the view scrolls under it near the edges, and the keyboard scrolls to the row it moves to. See https://slotsmith.dev/components/data-table/guides/virtual-rows/

### Can I render custom cells?

Yes. Columns are TanStack Table column definitions, so a column's cell function can return any React node. To change the cell element itself, replace the Cell slot. See https://slotsmith.dev/components/data-table/api/#slots

### Which TanStack Table version does it need?

TanStack Table 9: install @tanstack/react-table ^9.2.0 next to slotsmith. Virtual rows also need @tanstack/react-virtual ^3.10.0, whose useVirtualizer you pass in as virtual.use. See https://slotsmith.dev/components/data-table/#install
