tanstack-table
TanStack Table v8 headless data tables with server-side features for Cloudflare Workers + D1. Use for pagination, filtering, sorting, virtualization, or encountering state management, TanStack Query coordination, URL sync errors.
- 0
- Installs
- —
- Rating
- —
- Success rate
- 8
- Files scanned
Security scan
Scan passedNo risky patterns were found in the scanned files.
Content sha256 35b7c5402ba88afa… — run codexguild_scan_skills after installing to verify your local copy.
Static analysis is a first line of defense, not a guarantee. Read the source
SKILL.md
TanStack Table Skill
Build production-ready, headless data tables with TanStack Table v8, optimized for server-side patterns and Cloudflare Workers integration.
When to Use This Skill
Auto-triggers when you mention:
- "data table" or "datagrid"
- "server-side pagination" or "server-side filtering"
- "TanStack Table" or "React Table"
- "table with large dataset"
- "paginate/filter/sort with API"
- "Cloudflare D1 table integration"
- "virtualize table" or "large list performance"
Use this skill when:
- Building data tables with pagination, filtering, or sorting
- Implementing server-side table features (API-driven)
- Integrating tables with TanStack Query for data fetching
- Working with large datasets (1000+ rows) needing virtualization
- Connecting tables to Cloudflare D1 databases
- Need headless table logic without opinionated UI
- Migrating from other table libraries to TanStack Table v8
What This Skill Provides
1. Production Templates (7)
- Basic client-side table - Simple table with local data
- Server-paginated table - API-driven pagination with TanStack Query
- D1 database integration - Cloudflare D1 + Workers API + Table
- Column configuration patterns - Type-safe column definitions
- Controlled table state - Column visibility, pinning, ordering, fuzzy/global filtering, row selection
- Virtualized large dataset - Performance optimization with TanStack Virtual
- shadcn/ui styled table - Integration with Tailwind v4 + shadcn
2. Server-Side Patterns
- Pagination with API backends
- Filtering with query parameters
- Sorting with database queries
- State management (page, filters, sorting)
- URL synchronization
- TanStack Query coordination
3. Cloudflare Integration
- D1 database query patterns
- Workers API endpoints for table data
- Pagination + filtering + sorting in SQL
- Bindings setup (wrangler.jsonc)
- Client-side integration patterns
4. Performance Optimization
- Virtualization with TanStack Virtual
- Large dataset rendering (10k+ rows)
- Memory-efficient patterns
- useVirtualizer() integration
5. Feature Controls & UX
- Column visibility toggles and pinning (frozen columns)
- Column ordering and sizing defaults
- Global + fuzzy search and faceted filters
- Row selection and row pinning patterns
- Controlled state checklist to avoid perf regressions
6. Error Prevention
Documents and prevents 6+ common issues:
- Server-side state management confusion
- TanStack Query integration errors (query key coordination)
- Column filtering with API backends
- Manual sorting setup mistakes
- URL state synchronization issues
- Large dataset performance problems
- Over-controlling table state (columnSizingInfo) causing extra renders
Quick Start
Installation
# Core table library
bun add @tanstack/react-table@latest
# Optional: For virtualization (1000+ rows)
bun add @tanstack/react-virtual@latest
# Optional: For fuzzy/global search
bun add @tanstack/match-sorter-utils@latest
Latest verified versions (as of 2025-12-09):
@tanstack/react-table: v8.21.3 (stable)@tanstack/react-virtual: v3.13.23@tanstack/match-sorter-utils: v8.21.3 (for fuzzy filtering)
React support: Works on React 16.8+ through React 19; React Compiler is not supported.
Basic Client-Side Table
import { useReactTable, getCoreRowModel, ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
interface User {
id: string
name: string
email: string
}
const columns: ColumnDef<User>[] = [
{ accessorKey: 'id', header: 'ID' },
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
]
function UsersTable() {
// CRITICAL: Memoize data and columns to prevent infinite re-renders
const data = useMemo<User[]>(() => [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
], [])
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(), // Required
})
return (
<table>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th key={header.id}>
{header.isPlaceholder ? null : header.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 (Recommended for Large Datasets)
Pattern 1: Server-Side Pagination with TanStack Query
Cloudflare Workers API Endpoint:
// src/routes/api/users.ts
import { Env } from '../../types'
export async function onRequestGet(context: { request: Request; env: Env }) {
const url = new URL(context.request.url)
const page = Number(url.searchParams.get('page')) || 0
const pageSize = Number(url.searchParams.get('pageSize')) || 20
const offset = page * pageSize
// Query D1 database
const { results, meta } = await context.env.DB.prepare(`
SELECT id, name, email, created_at
FROM users
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`).bind(pageSize, offset).all()
// Get total count for pagination
const countResult = await context.env.DB.prepare(`
SELECT COUNT(*) as total FROM users
`).first<{ total: number }>()
return Response.json({
data: results,
pagination: {
page,
pageSize,
total: countResult?.total || 0,
pageCount: Math.ceil((countResult?.total || 0) / pageSize),
},
})
}
Client-Side Table with TanStack Query:
import { useReactTable, getCoreRowModel, PaginationState } from '@tanstack/react-table'
import { useQuery } from '@tanstack/react-query'
import { useState } from 'react'
function ServerPaginatedTable() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 20,
})
// TanStack Query fetches data
const { data, isLoading } = useQuery({
queryKey: ['users', pagination.pageIndex, pagination.pageSize],
queryFn: async () => {
const response = await fetch(
`/api/users?page=${pagination.pageIndex}&pageSize=${pagination.pageSize}`
)
return response.json()
},
})
// TanStack Table manages display
const table = useReactTable({
data: data?.data ?? [],
columns,
getCoreRowModel: getCoreRowModel(),
// Server-side pagination config
manualPagination: true, // CRITICAL: Tell table pagination is manual
pageCount: data?.pagination.pageCount ?? 0,
state: { pagination },
onPaginationChange: setPagination,
})
if (isLoading) return <div>Loading...</div>
return (
<div>
<table>{/* render table */}</table>
{/* Pagination controls */}
<div>
<button
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</button>
<span>
Page {table.getState().pagination.pageIndex + 1} of{' '}
{table.getPageCount()}
</span>
<button
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</button>
</div>
</div>
)
}
Pattern 2: Server-Side Filtering
API with Filter Support:
export async function onRequestGet(context: { request: Request; env: Env }) {
const url = new URL(context.request.url)
const search = url.searchParams.get('search') || ''
const { results } = await context.env.DB.prepare(`
SELECT * FROM users
WHERE name LIKE ? OR email LIKE ?
LIMIT 20
`).bind(`%${search}%`, `%${search}%`).all()
return Response.json({ data: results })
}
Client-Side:
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const { data } = useQuery({
queryKey: ['users', columnFilters],
queryFn: async () => {
const search = columnFilters.find(f => f.id === 'search')?.value || ''
return fetch(`/api/users?search=${search}`).then(r => r.json())
},
})
const table = useReactTable({
data: data?.data ?? [],
columns,
getCoreRowModel: getCoreRowModel(),
manualFiltering: true, // CRITICAL: Server handles filtering
state: { columnFilters },
onColumnFiltersChange: setColumnFilters,
})
Virtualization for Large Datasets
For 1000+ rows, use TanStack Virtual to only render visible rows:
import { useVirtualizer } from '@tanstack/react-virtual'
import { useRef } from 'react'
function VirtualizedTable() {
const tableContainerRef = useRef<HTMLDivElement>(null)
const table = useReactTable({
data: largeDataset, // 10k+ rows
columns,
getCoreRowModel: getCoreRowModel(),
})
const { rows } = table.getRowModel()
// Virtualize rows
const rowVirtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => tableContainerRef.current,
estimateSize: () => 50, // Row height in px
overscan: 10, // Render 10 extra rows for smooth scrolling
})
return (
<div ref={tableContainerRef} style={{ height: '600px', overflow: 'auto' }}>
<table style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
<thead>{/* header */}</thead>
<tbody>
{rowVirtualizer.getVirtualItems().map(virtualRow => {
const row = rows[virtualRow.index]
return (
<tr
key={row.id}
style={{
position: 'absolute',
transform: `translateY(${virtualRow.start}px)`,
width: '100%',
}}
>
{row.getVisibleCells().map(cell => (
<td key={cell.id}>{cell.renderValue()}</td>
))}
</tr>
)
})}
</tbody>
</table>
</div>
)
}
Common Errors & Solutions
Error 1: Infinite Re-Renders
Problem: Table re-renders infinitely, browser freezes.
Cause: data or columns references change on every render.
Solution: Always use useMemo or useState:
// ❌ BAD: New array reference every render
function Table() {
const data = [{ id: 1 }] // Creates new array!
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
}
// ✅ GOOD: Stable reference
function Table() {
const data = useMemo(() => [{ id: 1 }], []) // Stable
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
}
// ✅ ALSO GOOD: Define outside component
const data = [{ id: 1 }]
function Table() {
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
}
Error 2: TanStack Query + Table State Mismatch
Problem: Query refetches but pagination state not in sync, causing stale data.
Solution: Include ALL table state in query key:
// ❌ BAD: Missing pagination in query key
const { data } = useQuery({
queryKey: ['users'], // Doesn't include page!
queryFn: () => fetch(`/api/users?page=${pagination.pageIndex}`).then(r => r.json())
})
// ✅ GOOD: Complete query key
const { data } = useQuery({
queryKey: ['users', pagination.pageIndex, pagination.pageSize, columnFilters, sorting],
queryFn: () => {
const params = new URLSearchParams({
page: pagination.pageIndex.toString(),
pageSize: pagination.pageSize.toString(),
// ... filters, sorting
})
return fetch(`/api/users?${params}`).then(r => r.json())
}
})
Error 3: Server-Side Features Not Working
Problem: Pagination/filtering/sorting doesn't trigger API calls.
Solution: Set manual* flags to true:
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
// CRITICAL: Tell table these are server-side
manualPagination: true,
manualFiltering: true,
manualSorting: true,
pageCount: serverPageCount, // Must provide total page count
})
Error 4: TypeScript "Cannot Find Module" for Column Helper
Problem: Import errors for createColumnHelper.
Solution: Import from correct path:
// ❌ BAD: Wrong path
import { createColumnHelper } from '@tanstack/table-core'
// ✅ GOOD: Correct path
import { createColumnHelper } from '@tanstack/react-table'
// Usage for type-safe columns
const columnHelper = createColumnHelper<User>()
const columns = [
columnHelper.accessor('name', {
header: 'Name',
cell: info => info.getValue(), // Fully typed!
}),
]
Error 5: Sorting Not Working with Server-Side
Problem: Clicking sort headers doesn't update data.
Solution: Include sorting in query key and API call:
const [sorting, setSorting] = useState<SortingState>([])
const { data } = useQuery({
queryKey: ['users', pagination, sorting], // Include sorting
queryFn: async () => {
const sortParam = sorting[0]
? `&sortBy=${sorting[0].id}&sortOrder=${sorting[0].desc ? 'desc' : 'asc'}`
: ''
return fetch(`/api/users?page=${pagination.pageIndex}${sortParam}`).then(r => r.json())
}
})
const table = useReactTable({
data: data?.data ?? [],
columns,
getCoreRowModel: getCoreRowModel(),
manualSorting: true,
state: { sorting },
onSortingChange: setSorting,
})
Error 6: Poor Performance with Large Datasets
Problem: Table slow/laggy with 1000+ rows.
Solution: Use virtualization (see example above) or implement server-side pagination.
Integration with Existing Skills
With tanstack-query Skill
TanStack Table + TanStack Query is the recommended pattern:
// Query handles data fetching + caching
const { data, isLoading } = useQuery({
queryKey: ['users', tableState],
queryFn: fetchUsers,
})
// Table handles display + interactions
const table = useReactTable({
data: data?.data ?? [],
columns,
getCoreRowModel: getCoreRowModel(),
})
With cloudflare-d1 Skill
// Cloudflare Workers API (from cloudflare-d1 skill patterns)
export async function onRequestGet({ env }: { env: Env }) {
const { results } = await env.DB.prepare('SELECT * FROM users LIMIT 20').all()
return Response.json({ data: results })
}
// Client-side table consumes D1 data
const { data } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(r => r.json())
})
With tailwind-v4-shadcn Skill
Use shadcn/ui Table components with TanStack Table logic:
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table'
function StyledTable() {
const table = useReactTable({ /* config */ })
return (
<Table>
<TableHeader>
{table.getHeaderGroups().map(headerGroup => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map(header => (
<TableHead key={header.id}>
{header.column.columnDef.header}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map(row => (
<TableRow key={row.id}>
{row.getVisibleCells().map(cell => (
<TableCell key={cell.id}>
{cell.renderValue()}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
)
}
Best Practices
1. Always Memoize Data and Columns
const data = useMemo(() => [...], [dependencies])
const columns = useMemo(() => [...], [])
2. Use Server-Side for Large Datasets
- Client-side: <1000 rows
- Server-side: 1000+ rows or frequently changing data
3. Coordinate Query Keys with Table State
queryKey: ['resource', pagination, filters, sorting]
4. Provide Loading States
if (isLoading) return <TableSkeleton />
if (error) return <ErrorMessage error={error} />
5. Use Column Helper for Type Safety
const columnHelper = createColumnHelper<YourType>()
const columns = [
columnHelper.accessor('field', { /* fully typed */ })
]
6. Virtualize Large Client-Side Tables
if (data.length > 1000) {
// Use TanStack Virtual (see example above)
}
7. Control Only the State You Need
- Keep
sorting,pagination,filters,visibility,pinning,order,selectionin controlled state when you must persist or sync. - Avoid controlling
columnSizingInfounless persisting drag state; it triggers frequent updates and can hurt performance.
Templates Reference
All templates available in ~/.claude/skills/tanstack-table/templates/:
- package.json - Dependencies and versions
- basic-client-table.tsx - Simple client-side table
- server-paginated-table.tsx - Server-side pagination with Query
- d1-database-example.tsx - Cloudflare D1 integration
- column-configuration.tsx - Type-safe column patterns
- controlled-table-state.tsx - Visibility, pinning, ordering, fuzzy/global filtering, selection
- virtualized-large-dataset.tsx - Performance with Virtual
- shadcn-styled-table.tsx - Tailwind v4 + shadcn UI styling
Reference Docs
Deep-dive guides in ~/.claude/skills/tanstack-table/references/:
- server-side-patterns.md - Pagination, filtering, sorting with APIs
- query-integration.md - Coordinating with TanStack Query
- cloudflare-d1-examples.md - Workers + D1 complete examples
- performance-virtualization.md - TanStack Virtual guide
- common-errors.md - All 6+ documented issues with solutions
- feature-controls.md - Controlled state, visibility, pinning, ordering, fuzzy/global filtering, selection
When to Load References
Claude should suggest loading these reference files based on user needs:
Load references/common-errors.md when:
- User encounters infinite re-renders or table freezing
- Query data not syncing with pagination state changes
- Server-side features (pagination/filtering/sorting) not triggering API calls
- TypeScript errors with column helper imports
- Sorting state changes not updating API calls
- Performance problems with 1000+ rows client-side
- Any error message mentioned in the 6 documented issues
Load references/server-side-patterns.md when:
- User asks about implementing pagination with API backends
- Need to build filtering with backend query parameters
- Implementing sorting tied to database queries
- Building Cloudflare Workers or any API endpoints for table data
- Coordinating table state (page, filters, sort) with server calls
- Questions about manualPagination, manualFiltering, or manualSorting flags
Load references/query-integration.md when:
- Coordinating TanStack Table + TanStack Query together
- Query keys and table state synchronization issues
- Refetch patterns when pagination/filter/sort changes
- Query key composition with table state
- Stale data issues with server-side tables
Load references/cloudflare-d1-examples.md when:
- Building Cloudflare Workers API endpoints for table data
- Writing D1 database queries with pagination/filtering
- Need complete end-to-end Cloudflare integration examples
- SQL query patterns for table features (LIMIT/OFFSET, WHERE, ORDER BY)
- wrangler.jsonc bindings setup for D1 + table
Load references/performance-virtualization.md when:
- Working with 1000+ row datasets client-side
- TanStack Virtual integration questions
- Memory-efficient rendering patterns
- useVirtualizer() hook usage
- Large table performance optimization
- Questions about row virtualization or scroll performance
Load references/feature-controls.md when:
- Need column visibility, pinning, or ordering controls
- Building toolbars (global search, toggles) or syncing state to URL/localStorage
- Implementing fuzzy/global search or faceted filters
- Setting up row selection/pinning or controlled pagination/sorting
Token Efficiency
Without this skill:
- ~8,000 tokens: Research v8 changes, server-side patterns, Query integration
- 3-4 common errors encountered
- 30-45 minutes total time
With this skill:
- ~3,500 tokens: Direct templates, error prevention
- 0 errors (all documented issues prevented)
- 10-15 minutes total time
Savings: ~55-65% tokens, ~70% time
Production Validation
Tested with:
- React 19.2
- Vite 7.3
- TypeScript 5.9
- Cloudflare Workers (Wrangler 4.0)
- TanStack Query v5.101.4 (tanstack-query skill)
- Tailwind v4 + shadcn/ui (tailwind-v4-shadcn skill)
Stack compatibility:
- ✅ Cloudflare Workers + Static Assets
- ✅ Cloudflare D1 database
- ✅ TanStack Query integration
- ✅ React 19.2+ server components
- ✅ TypeScript strict mode
- ✅ Vite 7.3+ build optimization
Further Reading
- Official Docs: https://tanstack.com/table/latest
- TanStack Virtual: https://tanstack.com/virtual/latest
- GitHub: https://github.com/TanStack/table
- Cloudflare D1 Skill:
~/.claude/skills/cloudflare-d1/ - TanStack Query Skill:
~/.claude/skills/tanstack-query/
Last Updated: 2025-12-09 Skill Version: 1.1.0 Library Version: @tanstack/react-table v8.21.3
Files
8- SKILL.md
37f92d70d622.1 KB - references/cloudflare-d1-examples.md
ef6b78b69a3.4 KB - references/common-errors.md
06ac88afd74.5 KB - references/feature-controls.md
b20f9039f94.3 KB - references/performance-virtualization.md
3f83e7f3962.4 KB - references/query-integration.md
95dc5bdb215.1 KB - references/server-side-patterns.md
5f2a97b1bb8.9 KB - templates/package.json
a3cd84e813631 B
Agent reviews
0No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.
More from secondsky/claude-skills8
[TODO: Write comprehensive description in third-person. Start with "This skill provides..." or "This skill should be used when..."] [TODO: Add "Use when" scenarios - specific situations where Claude should use this skill] [TODO: Add keywords - technologies, use cases, error messages that should tr
100+ animated React components (Aceternity UI) for Next.js with Tailwind. Use for hero sections, parallax, 3D effects, or encountering animation, shadcn CLI integration errors.
Secure API authentication with JWT, OAuth 2.0, API keys. Use for authentication systems, third-party integrations, service-to-service communication, or encountering token management, security headers, auth flow errors.
Creates comprehensive API changelogs documenting breaking changes, deprecations, and migration strategies for API consumers. Use when managing API versions, communicating breaking changes, or creating upgrade guides.
Verifies API contracts between services using consumer-driven contracts, schema validation, and tools like Pact. Use when testing microservices communication, preventing breaking changes, or validating OpenAPI specifications.
Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers. Use when designing new APIs, reviewing API specifications, or establishing API design standards.
Implements standardized API error responses with proper status codes, logging, and user-friendly messages. Use when building production APIs, implementing error recovery patterns, or integrating error monitoring services.
Builds flexible API filtering and sorting systems with query parameter parsing, validation, and security. Use when implementing search endpoints, building data grids, or creating dynamic query APIs.
Related devops skillsscan passed
Use this skill to monitor and verify a deployed URL after releases — checks HTTP endpoints, SSE streams, static assets, console errors, and performance regressions after deploys, merges, or dependency upgrades. Smoke / canary / post-deploy verification.
Land and deploy workflow. (gstack)
Build, migrate, and deploy Next.js apps on Cloudflare Workers with vinext. Use when starting a Next.js project on Cloudflare, moving an existing app to Workers, choosing between vinext and OpenNext, or setting up vinext for Workers. For setup, migration, or deployment, install vinext's upstream skil
Deploy tRPC on AWS Lambda with awsLambdaRequestHandler() from @trpc/server/adapters/aws-lambda for API Gateway v1 (REST, APIGatewayProxyEvent) and v2 (HTTP, APIGatewayProxyEventV2), and Lambda Function URLs. Enable response streaming with awsLambdaStreamingRequestHandler() wrapped in awslambda.strea
Prepares production launches. Use when preparing to deploy to production, or when asking what needs to be in place before shipping. Use when you need a pre-launch checklist, when setting up monitoring, when planning a staged rollout, or when you need a rollback strategy.
Deploys and manages full-stack web applications (Next.js, Angular) with Server-Side Rendering (SSR) using Firebase App Hosting. Use when deploying Next.js/Angular apps, configuring apphosting.yaml or firebase.json apphosting blocks, managing secrets, setting up GitHub CI/CD, or configuring Blaze bil