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
Exploreagent - Server integration review: Use
Taskagent - Code review: Delegate to
code-revieweragent
If the
tanstack-queryskill is available, delegate data fetching, caching, and infinite query patterns to it. If thetanstack-virtualskill is available, delegate standalone virtualization patterns to it. If thetanstack-routerskill is available, delegate URL search param sync for server-side table state to it. If thetanstack-startskill 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