一键导入
react-table-patterns
TanStack React Table patterns. Activated when working with tables, DataGrid, column definitions, sorting, selection, or pagination.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
TanStack React Table patterns. Activated when working with tables, DataGrid, column definitions, sorting, selection, or pagination.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
AGENTS.md authoring patterns. Activated when writing AI agent context files, working with agents.md or AGENTS.md.
Codebase onboarding guide. Activated when starting a new project analysis, requesting systematic codebase understanding, or running /onboard.
Feature-Sliced Design architecture patterns. Activated when working with FSD layers, slices, dependency rules, or structuring frontend code.
Modern React 19+ patterns. Activated when working with state management, Suspense, Compound Components, or React hooks.
React Query server state management patterns. Activated when applying Query Factory, queryOptions, or mutationOptions patterns.
Practical TypeScript patterns. Activated when working with type inference, utility types, generics, type guards, or type narrowing.
| name | react-table-patterns |
| description | TanStack React Table patterns. Activated when working with tables, DataGrid, column definitions, sorting, selection, or pagination. |
@tanstack/react-table v8 DataGrid wrapper + column definition patterns
shared/ui/data-grid/
├── data-grid.tsx # DataGrid wrapper (useReactTable)
├── data-grid-header.tsx # Header group rendering
├── data-grid-body.tsx # Body (loading/empty states)
├── data-grid-row.tsx # Row (disabled overlay support)
├── data-grid-sortable-header.tsx
├── data-grid-loading.tsx # Loading skeleton
├── data-grid-empty.tsx # Empty state
└── types.ts # ColumnFlexBehavior type extension
features/{domain}/ui/
├── columns.tsx # Column definitions (ColumnDef[])
└── {domain}.table.tsx # Table component
DataGrid wraps useReactTable. Never call useReactTable directly.
import { DataGrid, Pagination } from '@/shared/ui';
import { getApplicantsColumns } from './columns';
export function ApplicantsTable() {
const { contents, pageable, isLoading } = useApplicantsList();
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const columns = getApplicantsColumns();
return (
<div className='flex-1 flex flex-col'>
<div className='flex-1 min-h-0'>
<DataGrid
columns={columns}
data={contents}
loading={isLoading}
emptyState={
<DataGrid.Empty
noDataContent={EMPTY_STATE.NO_DATA}
noSearchResultContent={EMPTY_STATE.NO_SEARCH_RESULT}
/>
}
enableRowSelection
rowSelection={rowSelection}
onRowSelectionChange={setRowSelection}
getRowId={(row) => row.applicantUserKey}
onRowClick={(row) => navigate({ to: '/detail/$id', params: { id: row.id } })}
/>
</div>
<Pagination pageable={pageable} />
</div>
);
}
import type { ColumnDef } from '@tanstack/react-table';
type ApplicantsColumn = NonNullable<
NonNullable<GetV1ApplicantsResponse['data']>['contents']
>[number];
export function getApplicantsColumns(): ColumnDef<ApplicantsColumn>[] {
return [
{
id: 'name',
accessorKey: 'name',
header: 'Name',
enableSorting: false,
meta: { flexBehavior: 'fluid' },
cell: ({ row }) => (
<div className='truncate'>{row.original.name}</div>
),
},
{
id: 'phone',
accessorKey: 'phone',
header: 'Phone',
enableSorting: false,
size: 120,
meta: { flexBehavior: 'fixed' },
cell: ({ row }) => (
<div className='truncate'>{row.original.phone || '-'}</div>
),
},
{
id: 'actions',
header: '',
enableSorting: false,
size: 80,
meta: { flexBehavior: 'fixed' },
cell: ({ row }) => <ActionMenu row={row.original} />,
},
];
}
interface GetColumnsParams {
processOptions: { label: string; value: string }[];
}
export function getPositionColumns({
processOptions,
}: GetColumnsParams): ColumnDef<PositionColumn>[] {
return [
{
id: 'processId',
header: 'Process',
cell: ({ row }) => (
<Select
items={processOptions}
value={row.original.processId}
onValueChange={(value) => handleChange(row.original.id, value)}
/>
),
},
];
}
// shared/ui/data-grid/types.ts
export type ColumnFlexBehavior = 'fixed' | 'fluid';
declare module '@tanstack/react-table' {
interface ColumnMeta<TData, TValue> {
flexBehavior?: ColumnFlexBehavior;
}
}
| Type | Usage | Examples |
|---|---|---|
'fixed' + size | Fixed width (shrink-0) | Checkbox (48), date (100), actions (80) |
'fluid' | Fills remaining space (flex-grow) | Name, description |
{
id: 'status',
cell: ({ row }) => {
const colorInfo = getStatusColorInfo(row.original.status);
return <Badge variant={colorInfo.variant} size='xsm'>{formatStatus(row.original.status)}</Badge>;
},
}
Use named function for cells that need hooks:
{
id: 'actions',
cell: function ActionCell({ row }) {
const { handleArchive, isPending } = useApplicantArchive(row.original.id);
const [isOpen, setIsOpen] = useState(false);
return (
<Menu open={isOpen} onOpenChange={setIsOpen}>
<MenuTrigger onClick={(e) => e.stopPropagation()}>
<Button size='icon' variant='primaryWhite'><EllipsisIcon /></Button>
</MenuTrigger>
<MenuContent align='end'>
<MenuItem onClick={handleArchive}>
{isPending ? <LoaderIcon className='animate-spin' /> : 'Delete'}
</MenuItem>
</MenuContent>
</Menu>
);
},
}
{
id: 'processId',
cell: ({ row }) => (
<div onClick={(e) => e.stopPropagation()}>
<Select
items={processOptions}
value={row.original.processId}
onValueChange={(value) => handleChange(row.original.id, value)}
/>
</div>
),
}
function TableWithSelection() {
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const selectedKeys = useMemo(() => Object.keys(rowSelection), [rowSelection]);
return (
<>
<DataGrid
columns={columns}
data={data}
enableRowSelection
rowSelection={rowSelection}
onRowSelectionChange={setRowSelection}
getRowId={(row) => row.id}
rowDisabled={(row) => row.status === 'LOCKED'}
/>
<FloatingBar selectedCount={selectedKeys.length} onClear={() => setRowSelection({})} />
</>
);
}
function TablePage() {
return (
<div className='flex-1 flex flex-col'>
<div className='px-9 py-6 shrink-0'>
<TableHeader totalCount={pageable?.total} />
</div>
<div className='flex-1 min-h-0'>
<DataGrid columns={columns} data={data} loading={isLoading}
emptyState={<DataGrid.Empty ... />} />
</div>
<div className='shrink-0 px-10 py-5 border-t'>
<Pagination pageable={pageable} />
</div>
<FloatingBar selectedCount={selectedCount} onClear={clearSelection} />
</div>
);
}
// Extract row type from API response
type ApplicantsColumn = NonNullable<
NonNullable<GetV1CompaniesApplicantsResponse['data']>['contents']
>[number];
// Required imports
import type { ColumnDef, RowSelectionState, SortingState, OnChangeFn } from '@tanstack/react-table';
import { getCoreRowModel, getSortedRowModel, flexRender, useReactTable } from '@tanstack/react-table';
// AVOID: direct useReactTable (use DataGrid wrapper)
const table = useReactTable({ ... });
// AVOID: inline column definitions
<DataGrid columns={[{ id: 'name', ... }]} data={data} />
// USE: separate columns file
import { getColumns } from './columns';
// AVOID: hooks in arrow function cell (Rules of Hooks violation)
cell: ({ row }) => { const { data } = useQuery(...); }
// USE: named function cell
cell: function MyCell({ row }) { const { data } = useQuery(...); }
// AVOID: size without flexBehavior
{ size: 120 }
// USE: pair with flexBehavior
{ size: 120, meta: { flexBehavior: 'fixed' } }
// AVOID: selection without getRowId
<DataGrid enableRowSelection rowSelection={rowSelection} />
// USE: always specify getRowId
<DataGrid enableRowSelection getRowId={(row) => row.uniqueKey} />
// AVOID: missing stopPropagation on interactive cells
cell: ({ row }) => (<Select onValueChange={handleChange} />)
// USE: wrap with stopPropagation
cell: ({ row }) => (
<div onClick={(e) => e.stopPropagation()}>
<Select onValueChange={handleChange} />
</div>
)