@tanstack/react-table 9.0.0-alpha.47 → 9.0.0-alpha.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/package.json +6 -4
- package/skills/react/client-to-server/SKILL.md +377 -0
- package/skills/react/compose-with-tanstack-form/SKILL.md +363 -0
- package/skills/react/compose-with-tanstack-pacer/SKILL.md +287 -0
- package/skills/react/compose-with-tanstack-query/SKILL.md +467 -0
- package/skills/react/compose-with-tanstack-store/SKILL.md +347 -0
- package/skills/react/compose-with-tanstack-virtual/SKILL.md +388 -0
- package/skills/react/compose-with-tanstack-virtual/references/column-virtualization-and-infinite-scroll.md +136 -0
- package/skills/react/getting-started/SKILL.md +388 -0
- package/skills/react/migrate-v8-to-v9/SKILL.md +488 -0
- package/skills/react/production-readiness/SKILL.md +341 -0
- package/skills/react/react-subscribe-compiler-compat/SKILL.md +269 -0
- package/skills/react/table-state/SKILL.md +432 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react/getting-started
|
|
3
|
+
description: >
|
|
4
|
+
End-to-end first-table journey for `@tanstack/react-table` v9. Install the
|
|
5
|
+
React adapter, declare `_features` via `tableFeatures()`, declare `_rowModels`
|
|
6
|
+
factories with their *Fns parameters (`createSortedRowModel(sortFns)` etc.),
|
|
7
|
+
create a column helper with both `TFeatures` and `TData` generics, instantiate
|
|
8
|
+
`useTable`, and render with `<table.FlexRender>`. New users land here, not on
|
|
9
|
+
`useLegacyTable`.
|
|
10
|
+
type: lifecycle
|
|
11
|
+
library: tanstack-table
|
|
12
|
+
framework: react
|
|
13
|
+
library_version: '9.0.0-alpha.47'
|
|
14
|
+
requires:
|
|
15
|
+
- setup
|
|
16
|
+
- column-definitions
|
|
17
|
+
- state-management
|
|
18
|
+
- react/table-state
|
|
19
|
+
sources:
|
|
20
|
+
- TanStack/table:docs/installation.md
|
|
21
|
+
- TanStack/table:docs/framework/react/react-table.md
|
|
22
|
+
- TanStack/table:examples/react/basic-use-table/src/main.tsx
|
|
23
|
+
- TanStack/table:examples/react/basic-use-app-table/src/main.tsx
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
This skill builds on `tanstack-table/state-management` and `tanstack-table/react/table-state`. Read those first — `_features` + `_rowModels` come from the core state-management concept, and `table-state` covers how reactivity flows in React.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add @tanstack/react-table
|
|
32
|
+
# or
|
|
33
|
+
npm install @tanstack/react-table
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`@tanstack/react-table` v9 requires React 18+ and TypeScript 5.4+ if you use TS.
|
|
37
|
+
|
|
38
|
+
## Minimum-viable v9 table
|
|
39
|
+
|
|
40
|
+
Three things are non-negotiable, even for the simplest possible table:
|
|
41
|
+
|
|
42
|
+
1. `_features: tableFeatures({...})` — required even if empty (`tableFeatures({})`).
|
|
43
|
+
2. `_rowModels: {...}` — required even if empty (`_rowModels: {}`). The **core** row model is automatic; you only register sorted/filtered/paginated/grouped/etc. when you use them.
|
|
44
|
+
3. `createColumnHelper<typeof _features, TData>()` — generic order is `<TFeatures, TData>` in v9 (changed from v8).
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import * as React from 'react'
|
|
48
|
+
import { useTable, tableFeatures } from '@tanstack/react-table'
|
|
49
|
+
import type { ColumnDef } from '@tanstack/react-table'
|
|
50
|
+
|
|
51
|
+
type Person = {
|
|
52
|
+
firstName: string
|
|
53
|
+
lastName: string
|
|
54
|
+
age: number
|
|
55
|
+
visits: number
|
|
56
|
+
status: string
|
|
57
|
+
progress: number
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 1. _features — required option, even if empty.
|
|
61
|
+
const _features = tableFeatures({})
|
|
62
|
+
|
|
63
|
+
// 2. Columns — defined at module scope for stable identity.
|
|
64
|
+
const columns: Array<ColumnDef<typeof _features, Person>> = [
|
|
65
|
+
{
|
|
66
|
+
accessorKey: 'firstName',
|
|
67
|
+
header: 'First Name',
|
|
68
|
+
cell: (info) => info.getValue(),
|
|
69
|
+
},
|
|
70
|
+
{ accessorKey: 'lastName', header: 'Last Name' },
|
|
71
|
+
{ accessorKey: 'age', header: 'Age' },
|
|
72
|
+
{ accessorKey: 'visits', header: 'Visits' },
|
|
73
|
+
{ accessorKey: 'status', header: 'Status' },
|
|
74
|
+
{ accessorKey: 'progress', header: 'Profile Progress' },
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
function App({ initialData }: { initialData: Person[] }) {
|
|
78
|
+
const [data] = React.useState(() => initialData)
|
|
79
|
+
|
|
80
|
+
// 3. Build the table — `_rowModels: {}` is required.
|
|
81
|
+
const table = useTable(
|
|
82
|
+
{
|
|
83
|
+
_features,
|
|
84
|
+
_rowModels: {},
|
|
85
|
+
columns,
|
|
86
|
+
data,
|
|
87
|
+
},
|
|
88
|
+
(state) => state, // default selector
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<table>
|
|
93
|
+
<thead>
|
|
94
|
+
{table.getHeaderGroups().map((hg) => (
|
|
95
|
+
<tr key={hg.id}>
|
|
96
|
+
{hg.headers.map((h) => (
|
|
97
|
+
<th key={h.id}>
|
|
98
|
+
{h.isPlaceholder ? null : <table.FlexRender header={h} />}
|
|
99
|
+
</th>
|
|
100
|
+
))}
|
|
101
|
+
</tr>
|
|
102
|
+
))}
|
|
103
|
+
</thead>
|
|
104
|
+
<tbody>
|
|
105
|
+
{table.getRowModel().rows.map((row) => (
|
|
106
|
+
<tr key={row.id}>
|
|
107
|
+
{row.getAllCells().map((cell) => (
|
|
108
|
+
<td key={cell.id}>
|
|
109
|
+
<table.FlexRender cell={cell} />
|
|
110
|
+
</td>
|
|
111
|
+
))}
|
|
112
|
+
</tr>
|
|
113
|
+
))}
|
|
114
|
+
</tbody>
|
|
115
|
+
</table>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Source: `examples/react/basic-use-table/src/main.tsx`.
|
|
121
|
+
|
|
122
|
+
## Adding sorting
|
|
123
|
+
|
|
124
|
+
Register the feature in `_features`, the factory in `_rowModels`, and wire a click handler:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import {
|
|
128
|
+
useTable,
|
|
129
|
+
tableFeatures,
|
|
130
|
+
rowSortingFeature,
|
|
131
|
+
createSortedRowModel,
|
|
132
|
+
sortFns,
|
|
133
|
+
createColumnHelper,
|
|
134
|
+
} from '@tanstack/react-table'
|
|
135
|
+
|
|
136
|
+
const _features = tableFeatures({ rowSortingFeature })
|
|
137
|
+
const columnHelper = createColumnHelper<typeof _features, Person>()
|
|
138
|
+
|
|
139
|
+
const columns = columnHelper.columns([
|
|
140
|
+
columnHelper.accessor('firstName', { header: 'First' }),
|
|
141
|
+
columnHelper.accessor('age', { header: 'Age' }),
|
|
142
|
+
])
|
|
143
|
+
|
|
144
|
+
function App({ data }: { data: Person[] }) {
|
|
145
|
+
const table = useTable({
|
|
146
|
+
_features,
|
|
147
|
+
_rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
148
|
+
columns,
|
|
149
|
+
data,
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<table>
|
|
154
|
+
<thead>
|
|
155
|
+
{table.getHeaderGroups().map((hg) => (
|
|
156
|
+
<tr key={hg.id}>
|
|
157
|
+
{hg.headers.map((h) => (
|
|
158
|
+
<th key={h.id} onClick={h.column.getToggleSortingHandler()}>
|
|
159
|
+
<table.FlexRender header={h} />
|
|
160
|
+
{{ asc: ' 🔼', desc: ' 🔽' }[
|
|
161
|
+
h.column.getIsSorted() as string
|
|
162
|
+
] ?? null}
|
|
163
|
+
</th>
|
|
164
|
+
))}
|
|
165
|
+
</tr>
|
|
166
|
+
))}
|
|
167
|
+
</thead>
|
|
168
|
+
{/* tbody same as above */}
|
|
169
|
+
</table>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`createSortedRowModel` REQUIRES `sortFns` (and the equivalents for `createFilteredRowModel(filterFns)`, `createGroupedRowModel(aggregationFns)`). The factory parameter is what makes the registry tree-shakeable.
|
|
175
|
+
|
|
176
|
+
## Layering features
|
|
177
|
+
|
|
178
|
+
Adding pagination and filtering is purely additive — register the feature + factory, and call the built-in APIs:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
const _features = tableFeatures({
|
|
182
|
+
rowSortingFeature,
|
|
183
|
+
rowPaginationFeature,
|
|
184
|
+
columnFilteringFeature,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const table = useTable({
|
|
188
|
+
_features,
|
|
189
|
+
_rowModels: {
|
|
190
|
+
sortedRowModel: createSortedRowModel(sortFns),
|
|
191
|
+
filteredRowModel: createFilteredRowModel(filterFns),
|
|
192
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
193
|
+
},
|
|
194
|
+
columns,
|
|
195
|
+
data,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Built-in APIs you should reach for, NOT reimplement:
|
|
199
|
+
table.setSorting([{ id: 'age', desc: true }])
|
|
200
|
+
table.nextPage()
|
|
201
|
+
table.setColumnFilters([{ id: 'firstName', value: 'tan' }])
|
|
202
|
+
column.toggleSorting()
|
|
203
|
+
row.toggleSelected()
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Source: `docs/framework/react/react-table.md`; `examples/react/basic-use-table/src/main.tsx`.
|
|
207
|
+
|
|
208
|
+
## Optional: `createTableHook` for shared config
|
|
209
|
+
|
|
210
|
+
If you ship the same `_features` / `_rowModels` / cell components across many tables, package them once:
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
import { createTableHook } from '@tanstack/react-table'
|
|
214
|
+
|
|
215
|
+
const { useAppTable, createAppColumnHelper } = createTableHook({
|
|
216
|
+
_features: {},
|
|
217
|
+
_rowModels: {},
|
|
218
|
+
debugTable: true,
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
const columnHelper = createAppColumnHelper<Person>()
|
|
222
|
+
const columns = columnHelper.columns([
|
|
223
|
+
columnHelper.accessor('firstName', { cell: (info) => info.getValue() }),
|
|
224
|
+
])
|
|
225
|
+
|
|
226
|
+
function App({ data }) {
|
|
227
|
+
const table = useAppTable({ columns, data })
|
|
228
|
+
// ... same FlexRender markup
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Source: `examples/react/basic-use-app-table/src/main.tsx`.
|
|
233
|
+
|
|
234
|
+
## Common Mistakes
|
|
235
|
+
|
|
236
|
+
### CRITICAL Forgetting `_features: tableFeatures({})`
|
|
237
|
+
|
|
238
|
+
Wrong:
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
const table = useTable({
|
|
242
|
+
_rowModels: {},
|
|
243
|
+
columns,
|
|
244
|
+
data,
|
|
245
|
+
})
|
|
246
|
+
// TS: Property '_features' is missing in type
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Correct:
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
const _features = tableFeatures({})
|
|
253
|
+
const table = useTable({ _features, _rowModels: {}, columns, data })
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The option is required even for a "no features" table — pass `tableFeatures({})` or `stockFeatures` if you want v8-style "everything on".
|
|
257
|
+
Source: `examples/react/basic-use-table/src/main.tsx`.
|
|
258
|
+
|
|
259
|
+
### CRITICAL Reimplementing what built-in APIs already provide
|
|
260
|
+
|
|
261
|
+
Wrong:
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
// Reimplements sorting state manually instead of using the API.
|
|
265
|
+
const [sorting, setSorting] = useState([])
|
|
266
|
+
const sortedData = useMemo(
|
|
267
|
+
() => [...data].sort((a, b) => /* custom */),
|
|
268
|
+
[data, sorting],
|
|
269
|
+
)
|
|
270
|
+
// uses sortedData directly, bypassing the table
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Correct:
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
const table = useTable({
|
|
277
|
+
_features: tableFeatures({ rowSortingFeature }),
|
|
278
|
+
_rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
279
|
+
columns,
|
|
280
|
+
data,
|
|
281
|
+
})
|
|
282
|
+
// Then: table.setSorting(...), column.toggleSorting(), header.getToggleSortingHandler()
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Maintainer flags this as the #1 tell that "an AI wrote this." The built-ins handle reset semantics, multi-sort, internal invariants.
|
|
286
|
+
Source: maintainer interview (Phase 4).
|
|
287
|
+
|
|
288
|
+
### CRITICAL API "missing" because the feature was not registered in `_features`
|
|
289
|
+
|
|
290
|
+
Wrong:
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
const _features = tableFeatures({}) // empty
|
|
294
|
+
const table = useTable({ _features, _rowModels: {}, columns, data })
|
|
295
|
+
table.setSorting([{ id: 'age', desc: true }]) // TS error — does not exist on this table type
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Correct:
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
const _features = tableFeatures({ rowSortingFeature })
|
|
302
|
+
const table = useTable({
|
|
303
|
+
_features,
|
|
304
|
+
_rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
305
|
+
columns,
|
|
306
|
+
data,
|
|
307
|
+
})
|
|
308
|
+
table.setSorting([{ id: 'age', desc: true }]) // ✓
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
In v9, `_features` is a tree-shakeable registry. If a feature isn't listed, TypeScript hides its APIs and the runtime atom is never created — the feature isn't broken, it's just not on.
|
|
312
|
+
Source: maintainer interview (Phase 4); `docs/framework/react/react-table.md`.
|
|
313
|
+
|
|
314
|
+
### HIGH Wrong generic order on `createColumnHelper`
|
|
315
|
+
|
|
316
|
+
Wrong:
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
const columnHelper = createColumnHelper<Person>() // v8 arity
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Correct:
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
const _features = tableFeatures({
|
|
326
|
+
/* … */
|
|
327
|
+
})
|
|
328
|
+
const columnHelper = createColumnHelper<typeof _features, Person>() // v9: <TFeatures, TData>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
v9 added `TFeatures` as the first generic across `Column`, `Row`, `ColumnDef`, `ColumnMeta`, etc. Use `typeof _features` so the same feature set drives types and runtime.
|
|
332
|
+
Source: `docs/framework/react/react-table.md`.
|
|
333
|
+
|
|
334
|
+
### HIGH Defining `_features` / `columns` / `data` inside the render body
|
|
335
|
+
|
|
336
|
+
Wrong:
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
function MyTable({ rows }) {
|
|
340
|
+
const _features = tableFeatures({ rowSortingFeature }) // new every render
|
|
341
|
+
const columns = [/* … */] // new every render
|
|
342
|
+
return <Table {/* … */} />
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Correct:
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
// Module scope = stable identity.
|
|
350
|
+
const _features = tableFeatures({ rowSortingFeature })
|
|
351
|
+
const columns: ColumnDef<typeof _features, Person>[] = [
|
|
352
|
+
/* … */
|
|
353
|
+
]
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Internal memoization keys off identity. A new object every render forces full recomputation and can cause subtle re-render issues.
|
|
357
|
+
Source: `examples/react/basic-use-table/src/main.tsx`; FAQ #1.
|
|
358
|
+
|
|
359
|
+
### HIGH Reaching for `useLegacyTable` for a new project
|
|
360
|
+
|
|
361
|
+
Wrong:
|
|
362
|
+
|
|
363
|
+
```tsx
|
|
364
|
+
import { useLegacyTable, getCoreRowModel } from '@tanstack/react-table/legacy'
|
|
365
|
+
const table = useLegacyTable({
|
|
366
|
+
columns,
|
|
367
|
+
data,
|
|
368
|
+
getCoreRowModel: getCoreRowModel(),
|
|
369
|
+
})
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Correct:
|
|
373
|
+
|
|
374
|
+
```tsx
|
|
375
|
+
import { useTable, tableFeatures } from '@tanstack/react-table'
|
|
376
|
+
const _features = tableFeatures({})
|
|
377
|
+
const table = useTable({ _features, _rowModels: {}, columns, data })
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`useLegacyTable` is a migration shim for incrementally upgrading v8 codebases. It bundles every feature, lacks `table.Subscribe`, and is deprecated in v9 / scheduled for removal in v10. New code uses `useTable`.
|
|
381
|
+
Source: `docs/framework/react/guide/use-legacy-table.md`.
|
|
382
|
+
|
|
383
|
+
## See Also
|
|
384
|
+
|
|
385
|
+
- `tanstack-table/react/table-state` — selectors, `<Subscribe>`, external atoms, `createTableHook`.
|
|
386
|
+
- `tanstack-table/react/migrate-v8-to-v9` — for codebases upgrading from `useReactTable`.
|
|
387
|
+
- `tanstack-table/react/production-readiness` — once it works, optimize for shipping.
|
|
388
|
+
- `tanstack-table/react/client-to-server` — when you outgrow client-side row processing.
|