Build headless data tables with TanStack Table v8. Server-side pagination, filtering, sorting, and virtualization for Cloudflare Workers + D1. Prevents 12 documented errors. Use when building tables with large datasets, coordinating with TanStack Query, or fixing state management, performance, or React 19+ compatibility issues.
View on GitHubSelect agents to install to:
npx add-skill https://github.com/jezweb/claude-skills/blob/main/skills/tanstack-table/SKILL.md -a claude-code --skill tanstack-tableInstallation paths:
.claude/skills/tanstack-table/# TanStack Table
Headless data tables with server-side pagination, filtering, sorting, and virtualization for Cloudflare Workers + D1
---
## Quick Start
**Last Updated**: 2026-01-09
**Versions**: @tanstack/react-table@8.21.3, @tanstack/react-virtual@3.13.18
```bash
npm install @tanstack/react-table@latest
npm install @tanstack/react-virtual@latest # For virtualization
```
**Basic Setup** (CRITICAL: memoize data/columns to prevent infinite re-renders):
```typescript
import { useReactTable, getCoreRowModel, ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
const columns: ColumnDef<User>[] = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
]
function UsersTable() {
const data = useMemo(() => [...users], []) // Stable reference
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
return (
<table>
<thead>
{table.getHeaderGroups().map(group => (
<tr key={group.id}>
{group.headers.map(h => <th key={h.id}>{h.column.columnDef.header}</th>)}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => (
<tr key={row.id}>
{row.getVisibleCells().map(cell => <td key={cell.id}>{cell.renderValue()}</td>)}
</tr>
))}
</tbody>
</table>
)
}
```
---
## Server-Side Patterns
**Cloudflare D1 API** (pagination + filtering + sorting):
```typescript
// Workers API: functions/api/users.ts
export async function onRequestGet({ request, env }) {
const url = new URL(request.url)
const page = Number(url.searchParams.get('page')) || 0
const pageSize = 20
const search = url.searchParams.get('search') || ''
const sortBy = url.searchParams.get('sortBy') || 'created_at'
const sortOrder = url.searchParams.get('sortOrder') || 'DESC'
const { results } = await env.DB.prepare(`
SELECT * FROM users
WHERE name LIKE ? OR email LIKE ?
ORDER BY