Quick Start
pnpm add @tanstack/react-table
import {
flexRender,
getCoreRowModel,
useReactTable,
type ColumnDef,
} from '@tanstack/react-table';
const columns: ColumnDef[] = [ { accessorKey: 'name', header: 'Name' }, { accessorKey: 'email', header: 'Email' }, ];
function DataTable({ data }: { data: Person[] }) { const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), });
return ( {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {flexRender( header.column.columnDef.header, header.getContext(), )} ))} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} ))} ); }
## Row Models
Import only what you need:
```tsx
import {
getCoreRowModel, // Required
getSortedRowModel, // Sorting
getFilteredRowModel, // Filtering
getPaginationRowModel, // Pagination
} from '@tanstack/react-table';
Column Definitions
// Basic
{ accessorKey: 'email', header: 'Email' }
// Computed value
{ accessorFn: (row) => `${row.first} ${row.last}`, id: 'fullName', header: 'Name' }
// Custom cell
{
accessorKey: 'amount',
cell: ({ row }) => formatCurrency(row.getValue('amount')),
}
// Actions column
{
id: 'actions',
cell: ({ row }) => <ActionMenu item={row.original} />,
}
State Management Pattern
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const table = useReactTable({
data,
columns,
state: { sorting, columnFilters, rowSelection },
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});
Common Operations
| Task | Method |
|---|---|
| Sort column | column.toggleSorting() |
| Filter column | column.setFilterValue(value) |
| Next page | table.nextPage() |
| Select row | row.toggleSelected() |
| Hide column | column.toggleVisibility() |
| Get original data | row.original |
Project Structure
apps/web/src/routes/payments/
├── columns.tsx # Column definitions
├── data-table.tsx # DataTable component
└── index.tsx # Route (fetches data)
Common Mistakes
| Mistake | Correct Pattern |
|---|---|
Missing getCoreRowModel |
Always required as base row model |
| Importing all row models | Only import what you use |
Not using flexRender |
Required for both static and dynamic content |
Missing key on rows |
Use row.id as key |
| Accessing data directly | Use row.original for the original data |
| Client pagination for large data | Use manualPagination: true for server-side |
| Missing table state | Control sorting/filtering via state props |
| Over-abstracting table | Each table is unique, don't create generic wrappers |
Delegation
- Pattern discovery: For finding existing table implementations, use
Exploreagent - Code review: After implementing tables, delegate to
code-revieweragent
Topic References
- Server Pagination - Server-side pagination, URL sync, faceted filters
- Infinite Scroll - Cursor pagination, intersection observer, virtual scrolling
- Column Definitions - Headers, cells, sorting, filtering, visibility
- Reusable Components - DataTable components, skeletons, full example