TanStack Table
Overview
TanStack Table is a headless table library — it provides state management and logic but no UI. You supply the rendering; it handles sorting, filtering, pagination, selection, and more.
When to use: Complex data tables with sorting/filtering/pagination, server-side data, large datasets (1000+ rows with virtualization), row selection/expanding/grouping.
When NOT to use: Simple static tables (use <table> directly), display-only lists (use a list component), spreadsheet-like editing (consider AG Grid).
Quick Reference
| Pattern |
API / Config |
Key Points |
| Basic table |
useReactTable({ data, columns, getCoreRowModel }) |
Memoize data/columns to prevent re-renders |
| Column helper |
createColumnHelper<T>() |
Type-safe column definitions |
| Column groups |
columnHelper.group({ header, columns }) |
Nested headers; don't pin group columns |
| Sorting |
getSortedRowModel() + onSortingChange |
manualSorting: true for server-side |
| Filtering |
getFilteredRowModel() + onColumnFiltersChange |
manualFiltering: true for server-side |
| Pagination |
getPaginationRowModel() + onPaginationChange |
manualPagination: true + pageCount |
| Row selection |
enableRowSelection + onRowSelectionChange |
Set getRowId for stable selection keys |
| Column visibility |
onColumnVisibilityChange |
Toggle with column.toggleVisibility() |
| Column pinning |
enableColumnPinning + initialState.columnPinning |
Don't pin group columns (known bug) |
| Row expanding |
getExpandedRowModel() + getSubRows |
For nested/tree data |
| Column resizing |
enableColumnResizing + columnResizeMode |
onChange for live, onEnd for performant |
| Row grouping |
getGroupedRowModel() + aggregationFn |
Performance degrades at 10k+ rows |
| Server-side |
manual*: true flags + include state in queryKey |
All state in query key for proper refetching |
| Infinite scroll |
useInfiniteQuery + flatten pages |
Combine with TanStack Virtual for best perf |
| Virtualization |
useVirtualizer from @tanstack/react-virtual |
Disable when container hidden (tabs/modals) |
| React 19 Compiler |
'use no memo' directive |
Required until v9 fixes compiler compat |
Common Operations
| Task |
Method |
| Sort column |
column.toggleSorting() |
| Filter column |
column.setFilterValue(value) |
| First page |
table.firstPage() |
| Next page |
table.nextPage() |
| Previous page |
table.previousPage() |
| Last page |
table.lastPage() |
| Go to page |
table.setPageIndex(n) |
| Select row |
row.toggleSelected() |
| Hide column |
column.toggleVisibility() |
| Get original data |
row.original |
| Pin column |
column.pin('left') |
| Resize column |
header.getResizeHandler() |
| Expand row |
row.toggleExpanded() |
Row Models
| Import |
Purpose |
getCoreRowModel |
Required |
getSortedRowModel |
Sorting |
getFilteredRowModel |
Filtering |
getPaginationRowModel |
Pagination |
getExpandedRowModel |
Expanding |
getGroupedRowModel |
Grouping |
getFacetedRowModel |
Faceted filter counts |
getFacetedUniqueValues |
Unique values per facet |
getFacetedMinMaxValues |
Min/max per facet |
Common Mistakes
| Mistake |
Correct Pattern |
| Unstable data/columns reference |
Memoize with useMemo or define outside component |
Missing manual* flags for server-side |
Set manualPagination, manualSorting, manualFiltering |
| Query key missing table state |
Include pagination, sorting, filters in queryKey |
Import from @tanstack/table-core |
Import from @tanstack/react-table |
Using v7 useTable / Header / accessor |
Use v8 useReactTable / header / accessorKey |
| Pinning group columns |
Pin individual columns within the group, not parent |
| Grouping on 10k+ rows |
Use server-side grouping or disable for large datasets |
| Column filter not clearing on page change |
Reset pageIndex to 0 when filters change |
Missing 'use no memo' with React Compiler |
Add directive to components using useReactTable |
Missing getRowId with row selection |
Set getRowId: (row) => row.id for stable selection keys |
| Filter value type mismatch |
Match value types; clear with undefined, not null |
Delegation
- Table pattern discovery: Use
Explore agent
- Server integration review: Use
Task agent
- Code review: Delegate to
code-reviewer agent
If the tanstack-query skill is available, delegate data fetching, caching, and infinite query patterns to it.
If the tanstack-virtual skill is available, delegate standalone virtualization patterns to it.
If the tanstack-router skill is available, delegate URL search param sync for server-side table state to it.
If the tanstack-start skill is available, delegate server functions for server-side data loading to it.
References
- Column definitions, helpers, visibility, and selection
- Filtering: column, global, fuzzy, and faceted
- Server-side patterns with TanStack Query
- Infinite scroll with cursor pagination
- Reusable table components (Shadcn-styled)
- Virtualization for large datasets
- Column and row pinning
- Row expanding and grouping
- Known issues and solutions (15 documented)
- v7 to v8 migration guide
1---2name: tanstack-table3description: TanStack Table v8 headless data tables for React. Covers column definitions, sorting, filtering (fuzzy/faceted), server-side pagination with TanStack Query, infinite scroll, virtualization (TanStack Virtual), column/row pinning, row expanding/grouping, column resizing, and reusable Shadcn-styled components. Prevents 15 documented errors including infinite re-renders, React Compiler incompatibility, and server-side state mismatches. Use when building data tables, fixing table performance, implementing server-side pagination, adding filtering/sorting, or debugging table state issues.4license: MIT5---6
7# TanStack Table
8
9## Overview
10
11TanStack Table is a **headless** table library — it provides state management and logic but no UI. You supply the rendering; it handles sorting, filtering, pagination, selection, and more.
12
13**When to use:** Complex data tables with sorting/filtering/pagination, server-side data, large datasets (1000+ rows with virtualization), row selection/expanding/grouping.
14
15**When NOT to use:** Simple static tables (use `<table>` directly), display-only lists (use a list component), spreadsheet-like editing (consider AG Grid).
16
17## Quick Reference
18
19| Pattern | API / Config | Key Points |
20| ----------------- | ---------------------------------------------------- | -------------------------------------------- |
21| Basic table | `useReactTable({ data, columns, getCoreRowModel })` | Memoize data/columns to prevent re-renders |
22| Column helper | `createColumnHelper<T>()` | Type-safe column definitions |
23| Column groups | `columnHelper.group({ header, columns })` | Nested headers; don't pin group columns |
24| Sorting | `getSortedRowModel()` + `onSortingChange` | `manualSorting: true` for server-side |
25| Filtering | `getFilteredRowModel()` + `onColumnFiltersChange` | `manualFiltering: true` for server-side |
26| Pagination | `getPaginationRowModel()` + `onPaginationChange` | `manualPagination: true` + `pageCount` |
27| Row selection | `enableRowSelection` + `onRowSelectionChange` | Set `getRowId` for stable selection keys |
28| Column visibility | `onColumnVisibilityChange` | Toggle with `column.toggleVisibility()` |
29| Column pinning | `enableColumnPinning` + `initialState.columnPinning` | Don't pin group columns (known bug) |
30| Row expanding | `getExpandedRowModel()` + `getSubRows` | For nested/tree data |
31| Column resizing | `enableColumnResizing` + `columnResizeMode` | `onChange` for live, `onEnd` for performant |
32| Row grouping | `getGroupedRowModel()` + `aggregationFn` | Performance degrades at 10k+ rows |
33| Server-side | `manual*: true` flags + include state in queryKey | All state in query key for proper refetching |
34| Infinite scroll | `useInfiniteQuery` + flatten pages | Combine with TanStack Virtual for best perf |
35| Virtualization | `useVirtualizer` from `@tanstack/react-virtual` | Disable when container hidden (tabs/modals) |
36| React 19 Compiler | `'use no memo'` directive | Required until v9 fixes compiler compat |
37
38## Common Operations
39
40| Task | Method |
41| ----------------- | ------------------------------ |
42| Sort column | `column.toggleSorting()` |
43| Filter column | `column.setFilterValue(value)` |
44| First page | `table.firstPage()` |
45| Next page | `table.nextPage()` |
46| Previous page | `table.previousPage()` |
47| Last page | `table.lastPage()` |
48| Go to page | `table.setPageIndex(n)` |
49| Select row | `row.toggleSelected()` |
50| Hide column | `column.toggleVisibility()` |
51| Get original data | `row.original` |
52| Pin column | `column.pin('left')` |
53| Resize column | `header.getResizeHandler()` |
54| Expand row | `row.toggleExpanded()` |
55
56## Row Models
57
58| Import | Purpose |
59| ------------------------ | ----------------------- |
60| `getCoreRowModel` | Required |
61| `getSortedRowModel` | Sorting |
62| `getFilteredRowModel` | Filtering |
63| `getPaginationRowModel` | Pagination |
64| `getExpandedRowModel` | Expanding |
65| `getGroupedRowModel` | Grouping |
66| `getFacetedRowModel` | Faceted filter counts |
67| `getFacetedUniqueValues` | Unique values per facet |
68| `getFacetedMinMaxValues` | Min/max per facet |
69
70## Common Mistakes
71
72| Mistake | Correct Pattern |
73| ------------------------------------------- | ---------------------------------------------------------- |
74| Unstable data/columns reference | Memoize with `useMemo` or define outside component |
75| Missing `manual*` flags for server-side | Set `manualPagination`, `manualSorting`, `manualFiltering` |
76| Query key missing table state | Include pagination, sorting, filters in queryKey |
77| Import from `@tanstack/table-core` | Import from `@tanstack/react-table` |
78| Using v7 `useTable` / `Header` / `accessor` | Use v8 `useReactTable` / `header` / `accessorKey` |
79| Pinning group columns | Pin individual columns within the group, not parent |
80| Grouping on 10k+ rows | Use server-side grouping or disable for large datasets |
81| Column filter not clearing on page change | Reset `pageIndex` to 0 when filters change |
82| Missing `'use no memo'` with React Compiler | Add directive to components using `useReactTable` |
83| Missing `getRowId` with row selection | Set `getRowId: (row) => row.id` for stable selection keys |
84| Filter value type mismatch | Match value types; clear with `undefined`, not `null` |
85
86## Delegation
87
88- **Table pattern discovery**: Use `Explore` agent
89- **Server integration review**: Use `Task` agent
90- **Code review**: Delegate to `code-reviewer` agent
91
92> If the `tanstack-query` skill is available, delegate data fetching, caching, and infinite query patterns to it.
93> If the `tanstack-virtual` skill is available, delegate standalone virtualization patterns to it.
94> If the `tanstack-router` skill is available, delegate URL search param sync for server-side table state to it.
95> If the `tanstack-start` skill is available, delegate server functions for server-side data loading to it.
96
97## References
98
99- [Column definitions, helpers, visibility, and selection](references/column-definitions.md)
100- [Filtering: column, global, fuzzy, and faceted](references/filtering.md)
101- [Server-side patterns with TanStack Query](references/server-side-patterns.md)
102- [Infinite scroll with cursor pagination](references/infinite-scroll.md)
103- [Reusable table components (Shadcn-styled)](references/reusable-components.md)
104- [Virtualization for large datasets](references/virtualization.md)
105- [Column and row pinning](references/column-row-pinning.md)
106- [Row expanding and grouping](references/expanding-grouping.md)
107- [Known issues and solutions (15 documented)](references/known-issues.md)
108- [v7 to v8 migration guide](references/v7-migration.md)