web-ui-tanstack-table

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack Table Patterns

TanStack Table 模式汇总

Quick Guide: TanStack Table is a headless UI library for building powerful tables and datagrids. Use
useReactTable
hook with
createColumnHelper
for type-safe column definitions. Import only the row models you need (
getSortedRowModel
,
getFilteredRowModel
, etc.) for tree-shaking. Memoize data and columns with
useMemo
to prevent infinite re-renders. Set
manualPagination
,
manualSorting
,
manualFiltering
to
true
for server-side data.

<critical_requirements>
快速指南: TanStack Table 是一个用于构建功能强大的表格和数据网格的无头UI库。结合
useReactTable
Hook 与
createColumnHelper
实现类型安全的列定义。仅导入所需的行模型(如
getSortedRowModel
getFilteredRowModel
等)以支持摇树优化。使用
useMemo
对数据和列进行记忆化处理,避免无限重渲染。处理服务端数据时,需将
manualPagination
manualSorting
manualFiltering
设置为
true

<critical_requirements>

CRITICAL: Before Using This Skill

重要提示:使用此技能之前

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST memoize data and columns with
useMemo
- unstable references cause infinite re-renders)
(You MUST use
createColumnHelper<TData>()
for type-safe column definitions with proper TValue inference)
(You MUST import row models explicitly -
getSortedRowModel
,
getFilteredRowModel
, etc. - for tree-shaking)
(You MUST use
accessorKey
for direct property access and
accessorFn
with explicit
id
for computed values)
(You MUST set
manualPagination
,
manualSorting
,
manualFiltering
to
true
for server-side data)
</critical_requirements>

Auto-detection: TanStack Table, @tanstack/react-table, useReactTable, createColumnHelper, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, ColumnDef, column definitions, table state
When to use:
  • Building data tables with sorting, filtering, and pagination
  • Implementing server-side data tables with API integration
  • Creating tables with row selection and expansion
  • Building virtual scrolling tables for large datasets
  • Implementing column visibility controls and column ordering
When NOT to use:
  • Simple tables without interactive features (use plain HTML tables)
  • Tables with fewer than 20 rows and no sorting/filtering needs
  • Read-only data display without user interaction
Key patterns covered:
  • useReactTable hook setup with type-safe generics
  • Column definitions with columnHelper
  • Sorting, filtering, pagination (client-side and server-side)
  • Row selection, expanding rows, column visibility
  • Virtual scrolling, column pinning, column resizing
Detailed Resources:
  • examples/core.md - Basic table setup, column definitions, type safety
  • examples/sorting.md - Column sorting with custom sort functions
  • examples/filtering.md - Column and global filtering
  • examples/pagination.md - Client-side pagination
  • examples/selection.md - Row selection with bulk actions
  • examples/expanding.md - Expandable rows with sub-content
  • examples/column-visibility.md - Column visibility toggles
  • examples/server-side.md - Server-side data handling
  • examples/virtualization.md - Virtual scrolling for large datasets
  • examples/column-pinning.md - Sticky pinned columns (left/right)
  • examples/column-resizing.md - Performant column resizing with CSS variables
  • reference.md - Decision frameworks, checklists, anti-patterns

<philosophy>
所有代码必须遵循 CLAUDE.md 中的项目规范(短横线命名、命名导出、导入顺序、
import type
、命名常量)
(必须使用
useMemo
对数据和列进行记忆化处理——不稳定的引用会导致无限重渲染)
(必须使用
createColumnHelper<TData>()
实现类型安全的列定义,并确保正确的 TValue 类型推断)
(必须显式导入行模型——如
getSortedRowModel
getFilteredRowModel
等——以支持摇树优化)
(直接属性访问必须使用
accessorKey
,计算值必须使用带显式
id
accessorFn
(处理服务端数据时,必须将
manualPagination
manualSorting
manualFiltering
设置为
true
</critical_requirements>

自动检测关键词: TanStack Table, @tanstack/react-table, useReactTable, createColumnHelper, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, ColumnDef, 列定义, 表格状态
适用场景:
  • 构建带有排序、筛选和分页功能的数据表格
  • 实现与API集成的服务端数据表格
  • 创建支持行选择和展开的表格
  • 为大型数据集构建虚拟滚动表格
  • 实现列可见性控制和列排序功能
不适用场景:
  • 无交互功能的简单表格(使用纯HTML表格即可)
  • 行数少于20行且无需排序/筛选的表格
  • 无用户交互的只读数据展示
涵盖的核心模式:
  • 带类型安全泛型的 useReactTable Hook 配置
  • 使用 columnHelper 定义列
  • 排序、筛选、分页(客户端和服务端)
  • 行选择、行展开、列可见性
  • 虚拟滚动、列固定、列宽调整
详细资源:
  • examples/core.md - 基础表格配置、列定义、类型安全
  • examples/sorting.md - 自定义排序函数的列排序
  • examples/filtering.md - 列筛选和全局筛选
  • examples/pagination.md - 客户端分页
  • examples/selection.md - 支持批量操作的行选择
  • examples/expanding.md - 带子内容的可展开行
  • examples/column-visibility.md - 列可见性切换
  • examples/server-side.md - 服务端数据处理
  • examples/virtualization.md - 大型数据集的虚拟滚动
  • examples/column-pinning.md - 固定列(左侧/右侧)
  • examples/column-resizing.md - 使用CSS变量实现高性能列宽调整
  • reference.md - 决策框架、检查清单、反模式

<philosophy>

Philosophy

设计理念

TanStack Table is a headless UI library - it provides the logic for tables without any markup or styles. This gives you complete control over rendering while the library handles complex state management for sorting, filtering, pagination, and more.
Core Principles:
  1. Headless Architecture - No pre-built components. You own the markup and styling.
  2. Type Safety - Full TypeScript support with generics for data types.
  3. Tree-Shakable - Import only what you use. Each feature is a separate row model.
  4. Framework Agnostic - Same API works across React, Vue, Solid, and Svelte.
  5. Performant - Optimized for large datasets with virtualization support.
Why Headless?
The headless approach means TanStack Table handles the hard parts (state management, sorting algorithms, pagination logic) while you control presentation. This is ideal when:
  • You need custom table designs that don't fit pre-built components
  • You're integrating with an existing design system
  • You need maximum performance control
</philosophy>
<patterns>
TanStack Table 是一个无头UI库——它仅提供表格的逻辑实现,不包含任何标记或样式。这让你完全控制渲染方式,同时由库处理排序、筛选、分页等复杂状态管理。
核心原则:
  1. 无头架构 - 无预构建组件,你完全掌控标记和样式
  2. 类型安全 - 全面支持TypeScript泛型,确保数据类型正确
  3. 摇树优化支持 - 仅导入所需功能,每个特性都是独立的行模型
  4. 框架无关 - 相同API可在React、Vue、Solid和Svelte中使用
  5. 高性能 - 针对大型数据集优化,支持虚拟滚动
为什么选择无头架构?
无头架构意味着TanStack Table处理复杂的部分(状态管理、排序算法、分页逻辑),而你负责展示层。这种方式非常适合以下场景:
  • 需要自定义表格设计,不适合使用预构建组件
  • 与现有设计系统集成
  • 需要最大程度控制性能
</philosophy>
<patterns>

Core Patterns

核心模式

Pattern 1: Basic Table Setup

模式1:基础表格配置

Set up a type-safe table with
useReactTable
and
createColumnHelper
. See examples/core.md for complete implementation.
typescript
const columnHelper = createColumnHelper<User>();

const columns = useMemo(
  () => [
    columnHelper.accessor("firstName", { header: "First Name" }),
    // accessorFn for computed values - MUST include id
    columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
      id: "fullName",
      header: "Full Name",
    }),
  ],
  [],
);

const data = useMemo(() => users, [users]);

const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getRowId: (row) => row.id,
});
Critical: Memoize both
columns
and
data
- unstable references cause infinite re-renders.

使用
useReactTable
createColumnHelper
搭建类型安全的表格。完整实现请参考 examples/core.md
typescript
const columnHelper = createColumnHelper<User>();

const columns = useMemo(
  () => [
    columnHelper.accessor("firstName", { header: "First Name" }),
    // accessorFn 用于计算值 - 必须包含 id
    columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
      id: "fullName",
      header: "Full Name",
    }),
  ],
  [],
);

const data = useMemo(() => users, [users]);

const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getRowId: (row) => row.id,
});
重要提示: 必须对
columns
data
进行记忆化处理——不稳定的引用会导致无限重渲染。

Pattern 2: Sorting

模式2:排序

Enable sorting with
getSortedRowModel
and controlled state. See examples/sorting.md.
typescript
const [sorting, setSorting] = useState<SortingState>([]);

const table = useReactTable({
  data,
  columns,
  state: { sorting },
  onSortingChange: setSorting,
  getCoreRowModel: getCoreRowModel(),
  getSortedRowModel: getSortedRowModel(),
});

// In column def:
columnHelper.accessor("createdAt", {
  header: "Created",
  sortingFn: "datetime", // Required for Date objects
});
Gotcha: Dates don't sort correctly with default sort. Use
sortingFn: "datetime"
for Date columns.

通过
getSortedRowModel
和受控状态启用排序。详细内容请参考 examples/sorting.md
typescript
const [sorting, setSorting] = useState<SortingState>([]);

const table = useReactTable({
  data,
  columns,
  state: { sorting },
  onSortingChange: setSorting,
  getCoreRowModel: getCoreRowModel(),
  getSortedRowModel: getSortedRowModel(),
});

// 在列定义中:
columnHelper.accessor("createdAt", {
  header: "Created",
  sortingFn: "datetime", // 处理Date对象时必填
});
注意事项: 默认排序对日期处理不正确,日期列需使用
sortingFn: "datetime"

Pattern 3: Filtering

模式3:筛选

Column filters and global filter with
getFilteredRowModel
. See examples/filtering.md.
typescript
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [globalFilter, setGlobalFilter] = useState("");

const table = useReactTable({
  data,
  columns,
  state: { columnFilters, globalFilter },
  onColumnFiltersChange: setColumnFilters,
  onGlobalFilterChange: setGlobalFilter,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
});
Gotcha: Multiple column filters combine with AND logic, not OR. Use global filter or custom logic for OR behavior.

通过
getFilteredRowModel
实现列筛选和全局筛选。详细内容请参考 examples/filtering.md
typescript
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [globalFilter, setGlobalFilter] = useState("");

const table = useReactTable({
  data,
  columns,
  state: { columnFilters, globalFilter },
  onColumnFiltersChange: setColumnFilters,
  onGlobalFilterChange: setGlobalFilter,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
});
注意事项: 多列筛选默认是AND逻辑,而非OR逻辑。如需OR逻辑,请使用全局筛选或自定义逻辑。

Pattern 4: Pagination

模式4:分页

Client-side and server-side pagination with
getPaginationRowModel
. See examples/pagination.md.
typescript
const DEFAULT_PAGE_SIZE = 10;

const [pagination, setPagination] = useState<PaginationState>({
  pageIndex: 0,
  pageSize: DEFAULT_PAGE_SIZE,
});

const table = useReactTable({
  data,
  columns,
  state: { pagination },
  onPaginationChange: setPagination,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
});
Gotcha:
pageIndex
is 0-based internally, but many APIs are 1-based. Add 1 when sending to server.

通过
getPaginationRowModel
实现客户端和服务端分页。详细内容请参考 examples/pagination.md
typescript
const DEFAULT_PAGE_SIZE = 10;

const [pagination, setPagination] = useState<PaginationState>({
  pageIndex: 0,
  pageSize: DEFAULT_PAGE_SIZE,
});

const table = useReactTable({
  data,
  columns,
  state: { pagination },
  onPaginationChange: setPagination,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
});
注意事项:
pageIndex
内部是0起始,但很多API是1起始,发送到服务端时需加1。

Pattern 5: Row Selection

模式5:行选择

Single and multi-row selection. See examples/selection.md.
typescript
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

const table = useReactTable({
  data,
  columns,
  state: { rowSelection },
  onRowSelectionChange: setRowSelection,
  getCoreRowModel: getCoreRowModel(),
  enableRowSelection: true,
  getRowId: (row) => row.id, // CRITICAL: Stable IDs for selection
});
Critical: Without
getRowId
, selection uses array indices which break when data is re-ordered or filtered.

实现单行和多行选择。详细内容请参考 examples/selection.md
typescript
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

const table = useReactTable({
  data,
  columns,
  state: { rowSelection },
  onRowSelectionChange: setRowSelection,
  getCoreRowModel: getCoreRowModel(),
  enableRowSelection: true,
  getRowId: (row) => row.id, // 重要提示:选择功能需要稳定的ID
});
重要提示: 如果不设置
getRowId
,选择功能会使用数组索引,当数据重新排序或筛选时会失效。

Pattern 6: Server-Side Data

模式6:服务端数据

Handle server-side pagination, sorting, and filtering. See examples/server-side.md.
typescript
const table = useReactTable({
  data: apiData ?? [],
  columns,
  state: { pagination, sorting, columnFilters },
  onPaginationChange: setPagination,
  onSortingChange: setSorting,
  onColumnFiltersChange: setColumnFilters,
  getCoreRowModel: getCoreRowModel(),
  // CRITICAL: All three manual flags for server-side
  manualPagination: true,
  manualSorting: true,
  manualFiltering: true,
  rowCount: totalFromApi,
});
Critical: Do NOT import client-side row models (
getSortedRowModel
, etc.) with
manual*: true
- they are redundant.

处理服务端分页、排序和筛选。详细内容请参考 examples/server-side.md
typescript
const table = useReactTable({
  data: apiData ?? [],
  columns,
  state: { pagination, sorting, columnFilters },
  onPaginationChange: setPagination,
  onSortingChange: setSorting,
  onColumnFiltersChange: setColumnFilters,
  getCoreRowModel: getCoreRowModel(),
  // 重要提示:服务端处理时必须设置这三个手动标记
  manualPagination: true,
  manualSorting: true,
  manualFiltering: true,
  rowCount: totalFromApi,
});
重要提示: 当设置
manual*: true
时,不要导入客户端行模型(如
getSortedRowModel
等)——它们是冗余的。

Pattern 7: Column Visibility

模式7:列可见性

Toggle column visibility. See examples/column-visibility.md.
typescript
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
  email: false, // Hide by default
});

// In column def - prevent hiding required columns:
columnHelper.accessor("id", { enableHiding: false });

切换列的可见性。详细内容请参考 examples/column-visibility.md
typescript
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
  email: false, // 默认隐藏
});

// 在列定义中 - 禁止隐藏必填列:
columnHelper.accessor("id", { enableHiding: false });

Pattern 8: Expanding Rows

模式8:行展开

Expandable rows for hierarchical data or detail views. See examples/expanding.md.
typescript
const [expanded, setExpanded] = useState<ExpandedState>({});

const table = useReactTable({
  data,
  columns,
  state: { expanded },
  onExpandedChange: setExpanded,
  getCoreRowModel: getCoreRowModel(),
  getExpandedRowModel: getExpandedRowModel(),
  getRowCanExpand: () => true,
});

为层级数据或详情视图实现可展开行。详细内容请参考 examples/expanding.md
typescript
const [expanded, setExpanded] = useState<ExpandedState>({});

const table = useReactTable({
  data,
  columns,
  state: { expanded },
  onExpandedChange: setExpanded,
  getCoreRowModel: getCoreRowModel(),
  getExpandedRowModel: getExpandedRowModel(),
  getRowCanExpand: () => true,
});

Pattern 9: Reusable Generic Table Component

模式9:可复用泛型表格组件

Leverage TypeScript generics for a reusable table component. See examples/core.md.
typescript
interface DataTableProps<TData, TValue> {
  columns: ColumnDef<TData, TValue>[];
  data: TData[];
}

export function DataTable<TData, TValue>({
  columns,
  data,
}: DataTableProps<TData, TValue>) {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  });
  // ... render table
}

利用TypeScript泛型实现可复用表格组件。详细内容请参考 examples/core.md
typescript
interface DataTableProps<TData, TValue> {
  columns: ColumnDef<TData, TValue>[];
  data: TData[];
}

export function DataTable<TData, TValue>({
  columns,
  data,
}: DataTableProps<TData, TValue>) {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  });
  // ... 渲染表格
}

Pattern 10: Column Pinning

模式10:列固定

Keep columns visible during horizontal scroll. See examples/column-pinning.md.
typescript
const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({
  left: ["id"],
  right: ["actions"],
});
Critical: Pinning provides state only. You must apply
position: sticky
and background CSS yourself to prevent content overlap.

在水平滚动时保持列可见。详细内容请参考 examples/column-pinning.md
typescript
const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({
  left: ["id"],
  right: ["actions"],
});
重要提示: 固定仅提供状态,你需要自己设置
position: sticky
和背景CSS来防止内容重叠。

Pattern 11: Column Resizing

模式11:列宽调整

User-adjustable column widths. See examples/column-resizing.md.
typescript
const table = useReactTable({
  data,
  columns,
  columnResizeMode: "onChange", // or "onEnd" for simpler, more performant
  enableColumnResizing: true,
});
Critical:
columnResizeMode: "onChange"
requires CSS variables pattern and memoized table body for 60fps performance. Use
"onEnd"
for simpler cases.
</patterns>
<red_flags>
支持用户调整列宽。详细内容请参考 examples/column-resizing.md
typescript
const table = useReactTable({
  data,
  columns,
  columnResizeMode: "onChange", // 或使用 "onEnd" 实现更简单、高性能的调整
  enableColumnResizing: true,
});
重要提示:
columnResizeMode: "onChange"
需要CSS变量模式和记忆化的表格主体才能达到60fps的性能。简单场景建议使用
"onEnd"
</patterns>
<red_flags>

RED FLAGS

注意事项

High Priority Issues:
  • Missing useMemo on columns/data - Columns or data defined inline without memoization cause infinite re-renders. Must be memoized or defined outside the component.
  • accessorFn without id - Using
    accessorFn
    without providing an
    id
    causes runtime errors.
  • Missing manualPagination for server-side - Forgetting
    manualPagination: true
    when using server-side data causes the table to paginate already-paginated data.
  • Returning JSX from accessorFn - Accessors return primitive values for sorting/filtering. Use the
    cell
    option for JSX rendering.
Medium Priority Issues:
  • Not providing rowCount for server-side - Without
    rowCount
    or
    pageCount
    , the table cannot calculate correct page count.
  • Missing getRowId with selection - Without
    getRowId
    , row selection uses array indices which break on sort/filter.
  • Not using flexRender - Manually rendering header/cell values breaks when columnDef uses a function for header/cell.
Gotchas & Edge Cases:
  • Date sorting requires
    sortingFn: "datetime"
    - JavaScript dates don't sort correctly by default
  • Column filters are AND, not OR - Multiple column filters combine with AND logic
  • pageIndex
    is 0-based - Many APIs use 1-based; add 1 when sending to server
  • autoResetPageIndex
    defaults to
    true
    - Page resets to 0 when data changes; set to
    false
    for server-side
  • Column pinning requires sticky CSS - TanStack Table provides state only, you apply CSS
  • columnResizeMode: "onChange"
    needs CSS variables + memoized body for performance
  • Attach
    getResizeHandler
    to both
    onMouseDown
    and
    onTouchStart
    for mobile support
  • Pinning affects column order - Pinning, column ordering, and grouping all reorder columns; pinning happens first
  • Pinned cells need background color - Otherwise scrolling content shows through
</red_flags>

<critical_reminders>
高优先级问题:
  • 未对列/数据使用useMemo - 内联定义列或数据而不进行记忆化会导致无限重渲染,必须进行记忆化或在组件外部定义
  • accessorFn未设置id - 使用
    accessorFn
    但未提供
    id
    会导致运行时错误
  • 服务端处理未设置manualPagination - 处理服务端数据时忘记设置
    manualPagination: true
    会导致表格对已分页的数据再次分页
  • accessorFn返回JSX - 访问器应返回原始值用于排序/筛选,如需渲染JSX请使用
    cell
    选项
中优先级问题:
  • 服务端处理未提供rowCount - 没有
    rowCount
    pageCount
    ,表格无法计算正确的页数
  • 选择功能未设置getRowId - 没有
    getRowId
    ,行选择会使用数组索引,在排序/筛选时会失效
  • 未使用flexRender - 手动渲染表头/单元格值会破坏列定义中使用函数的表头/单元格逻辑
注意事项与边缘情况:
  • 日期排序需要
    sortingFn: "datetime"
    - JavaScript日期默认排序不正确
  • 列筛选是AND逻辑,而非OR逻辑 - 多列筛选默认是AND组合
  • pageIndex
    是0起始 - 很多API使用1起始,发送到服务端时需加1
  • autoResetPageIndex
    默认是
    true
    - 数据变化时页面会重置为0,服务端处理时需设置为
    false
  • 列固定需要sticky CSS - TanStack Table仅提供状态,需自行应用CSS
  • columnResizeMode: "onChange"
    需要CSS变量+记忆化主体才能保证性能
  • 移动端支持需将
    getResizeHandler
    同时绑定到
    onMouseDown
    onTouchStart
  • 列固定会影响列顺序 - 固定、列排序和分组都会改变列顺序,固定操作优先级最高
  • 固定单元格需要背景色 - 否则滚动内容会透显
</red_flags>

<critical_reminders>

CRITICAL REMINDERS

重要提醒

All code must follow project conventions in CLAUDE.md
(You MUST memoize data and columns with
useMemo
- unstable references cause infinite re-renders)
(You MUST use
createColumnHelper<TData>()
for type-safe column definitions with proper TValue inference)
(You MUST import row models explicitly -
getSortedRowModel
,
getFilteredRowModel
, etc. - for tree-shaking)
(You MUST use
accessorKey
for direct property access and
accessorFn
with explicit
id
for computed values)
(You MUST set
manualPagination
,
manualSorting
,
manualFiltering
to
true
for server-side data)
Failure to follow these rules will cause infinite re-renders, TypeScript errors, and incorrect server-side behavior.
</critical_reminders>
所有代码必须遵循 CLAUDE.md 中的项目规范
(必须使用
useMemo
对数据和列进行记忆化处理——不稳定的引用会导致无限重渲染)
(必须使用
createColumnHelper<TData>()
实现类型安全的列定义,并确保正确的 TValue 类型推断)
(必须显式导入行模型——如
getSortedRowModel
getFilteredRowModel
等——以支持摇树优化)
(直接属性访问必须使用
accessorKey
,计算值必须使用带显式
id
accessorFn
(处理服务端数据时,必须将
manualPagination
manualSorting
manualFiltering
设置为
true
不遵循这些规则会导致无限重渲染、TypeScript错误和服务端行为异常。
</critical_reminders>