@tanstack/table-core 9.0.0-beta.37 → 9.0.0-beta.42

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.
Files changed (100) hide show
  1. package/README.md +1 -0
  2. package/dist/core/headers/buildHeaderGroups.cjs.map +1 -1
  3. package/dist/core/headers/buildHeaderGroups.d.cts +1 -1
  4. package/dist/core/headers/buildHeaderGroups.d.ts +1 -1
  5. package/dist/core/headers/buildHeaderGroups.js.map +1 -1
  6. package/dist/core/headers/coreHeadersFeature.utils.cjs +7 -7
  7. package/dist/core/headers/coreHeadersFeature.utils.cjs.map +1 -1
  8. package/dist/core/headers/coreHeadersFeature.utils.js +7 -7
  9. package/dist/core/headers/coreHeadersFeature.utils.js.map +1 -1
  10. package/dist/core/table/coreTablesFeature.utils.cjs +1 -1
  11. package/dist/core/table/coreTablesFeature.utils.cjs.map +1 -1
  12. package/dist/core/table/coreTablesFeature.utils.js +1 -1
  13. package/dist/core/table/coreTablesFeature.utils.js.map +1 -1
  14. package/dist/features/column-ordering/columnOrderingFeature.types.d.cts +5 -5
  15. package/dist/features/column-ordering/columnOrderingFeature.types.d.ts +5 -5
  16. package/dist/features/column-ordering/columnOrderingFeature.utils.cjs +6 -6
  17. package/dist/features/column-ordering/columnOrderingFeature.utils.cjs.map +1 -1
  18. package/dist/features/column-ordering/columnOrderingFeature.utils.d.cts +3 -3
  19. package/dist/features/column-ordering/columnOrderingFeature.utils.d.ts +3 -3
  20. package/dist/features/column-ordering/columnOrderingFeature.utils.js +6 -6
  21. package/dist/features/column-ordering/columnOrderingFeature.utils.js.map +1 -1
  22. package/dist/features/column-pinning/columnPinningFeature.cjs +44 -39
  23. package/dist/features/column-pinning/columnPinningFeature.cjs.map +1 -1
  24. package/dist/features/column-pinning/columnPinningFeature.d.cts +6 -1
  25. package/dist/features/column-pinning/columnPinningFeature.d.ts +6 -1
  26. package/dist/features/column-pinning/columnPinningFeature.js +45 -40
  27. package/dist/features/column-pinning/columnPinningFeature.js.map +1 -1
  28. package/dist/features/column-pinning/columnPinningFeature.types.d.cts +49 -38
  29. package/dist/features/column-pinning/columnPinningFeature.types.d.ts +49 -38
  30. package/dist/features/column-pinning/columnPinningFeature.utils.cjs +154 -146
  31. package/dist/features/column-pinning/columnPinningFeature.utils.cjs.map +1 -1
  32. package/dist/features/column-pinning/columnPinningFeature.utils.d.cts +81 -73
  33. package/dist/features/column-pinning/columnPinningFeature.utils.d.ts +81 -73
  34. package/dist/features/column-pinning/columnPinningFeature.utils.js +141 -133
  35. package/dist/features/column-pinning/columnPinningFeature.utils.js.map +1 -1
  36. package/dist/features/column-sizing/columnSizingFeature.cjs +4 -4
  37. package/dist/features/column-sizing/columnSizingFeature.cjs.map +1 -1
  38. package/dist/features/column-sizing/columnSizingFeature.js +5 -5
  39. package/dist/features/column-sizing/columnSizingFeature.js.map +1 -1
  40. package/dist/features/column-sizing/columnSizingFeature.types.d.cts +18 -12
  41. package/dist/features/column-sizing/columnSizingFeature.types.d.ts +18 -12
  42. package/dist/features/column-sizing/columnSizingFeature.utils.cjs +24 -20
  43. package/dist/features/column-sizing/columnSizingFeature.utils.cjs.map +1 -1
  44. package/dist/features/column-sizing/columnSizingFeature.utils.d.cts +18 -14
  45. package/dist/features/column-sizing/columnSizingFeature.utils.d.ts +18 -14
  46. package/dist/features/column-sizing/columnSizingFeature.utils.js +24 -20
  47. package/dist/features/column-sizing/columnSizingFeature.utils.js.map +1 -1
  48. package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs +16 -16
  49. package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs.map +1 -1
  50. package/dist/features/column-visibility/columnVisibilityFeature.utils.d.cts +3 -3
  51. package/dist/features/column-visibility/columnVisibilityFeature.utils.d.ts +3 -3
  52. package/dist/features/column-visibility/columnVisibilityFeature.utils.js +16 -16
  53. package/dist/features/column-visibility/columnVisibilityFeature.utils.js.map +1 -1
  54. package/dist/static-functions.cjs +16 -16
  55. package/dist/static-functions.d.cts +3 -3
  56. package/dist/static-functions.d.ts +3 -3
  57. package/dist/static-functions.js +3 -3
  58. package/package.json +1 -1
  59. package/skills/api-not-found/SKILL.md +113 -0
  60. package/skills/client-vs-server/SKILL.md +164 -0
  61. package/skills/column-faceting/SKILL.md +91 -0
  62. package/skills/column-filtering/SKILL.md +82 -0
  63. package/skills/column-ordering/SKILL.md +75 -0
  64. package/skills/column-pinning/SKILL.md +89 -0
  65. package/skills/column-resizing/SKILL.md +91 -0
  66. package/skills/column-sizing/SKILL.md +72 -0
  67. package/skills/column-visibility/SKILL.md +75 -0
  68. package/skills/core/SKILL.md +140 -0
  69. package/skills/custom-features/SKILL.md +207 -0
  70. package/skills/expanding/SKILL.md +80 -0
  71. package/skills/global-filtering/SKILL.md +84 -0
  72. package/skills/grouping/SKILL.md +50 -394
  73. package/skills/migrate-v8-to-v9/SKILL.md +230 -390
  74. package/skills/pagination/SKILL.md +35 -344
  75. package/skills/row-pinning/SKILL.md +47 -238
  76. package/skills/row-selection/SKILL.md +39 -351
  77. package/skills/sorting/SKILL.md +35 -299
  78. package/skills/table-features/SKILL.md +153 -0
  79. package/skills/typescript/SKILL.md +126 -0
  80. package/src/core/headers/buildHeaderGroups.ts +1 -1
  81. package/src/core/headers/coreHeadersFeature.utils.ts +7 -7
  82. package/src/core/table/coreTablesFeature.utils.ts +1 -1
  83. package/src/features/column-ordering/columnOrderingFeature.types.ts +5 -5
  84. package/src/features/column-ordering/columnOrderingFeature.utils.ts +9 -9
  85. package/src/features/column-pinning/columnPinningFeature.ts +64 -59
  86. package/src/features/column-pinning/columnPinningFeature.types.ts +49 -38
  87. package/src/features/column-pinning/columnPinningFeature.utils.ts +163 -155
  88. package/src/features/column-sizing/columnSizingFeature.ts +6 -6
  89. package/src/features/column-sizing/columnSizingFeature.types.ts +18 -12
  90. package/src/features/column-sizing/columnSizingFeature.utils.ts +31 -27
  91. package/src/features/column-visibility/columnVisibilityFeature.utils.ts +15 -15
  92. package/skills/column-definitions/SKILL.md +0 -330
  93. package/skills/column-layout/SKILL.md +0 -326
  94. package/skills/column-layout/references/subsystems.md +0 -220
  95. package/skills/customizing-feature-behavior/SKILL.md +0 -423
  96. package/skills/filtering/SKILL.md +0 -375
  97. package/skills/filtering/references/faceting-and-fuzzy.md +0 -218
  98. package/skills/row-expanding/SKILL.md +0 -356
  99. package/skills/setup/SKILL.md +0 -390
  100. package/skills/state-management/SKILL.md +0 -403
@@ -1,388 +1,76 @@
1
1
  ---
2
2
  name: row-selection
3
3
  description: >
4
- Track which rows are selected in TanStack Table v9 via
5
- `rowSelection: Record<rowId, boolean | undefined>`. Covers `rowSelectionFeature`,
6
- the three selected-row APIs (`getSelectedRowModel`, `getFilteredSelectedRowModel`,
7
- `getGroupedSelectedRowModel`), `row.toggleSelected` / `getIsSelected` /
8
- `getIsSomeSelected` (indeterminate) / `getCanSelect` / `getCanMultiSelect`,
9
- `row.getToggleSelectedHandler`, header APIs (`getIsAllRowsSelected` /
10
- `getIsSomeRowsSelected` / `getToggleAllRowsSelectedHandler` and the page-aware
11
- variants), `enableRowSelection` (bool or predicate),
12
- `enableMultiRowSelection: false` for radio-style, `enableSubRowSelection`,
13
- and why `getRowId` is essentially mandatory — especially under server pagination.
14
- type: core
15
- library: tanstack-table
16
- library_version: '9.0.0-alpha.48'
17
- requires:
18
- - state-management
19
- - column-definitions
4
+ Maintain rowSelection ID state with stable getRowId, single, multi, and subrow rules, selected row models, and manual-pagination semantics. Load when selected IDs outlive loaded Row objects or data removal.
5
+ metadata:
6
+ {
7
+ type: sub-skill,
8
+ library: '@tanstack/table-core',
9
+ library_version: '9.0.0-beta.42',
10
+ }
11
+ requires: ['core', 'table-features']
20
12
  sources:
21
- - TanStack/table:docs/guide/row-selection.md
22
- - TanStack/table:packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
23
- - TanStack/table:packages/table-core/src/features/row-selection/rowSelectionFeature.types.ts
24
- - TanStack/table:examples/react/row-selection/src/main.tsx
13
+ - 'TanStack/table:docs/framework/react/guide/row-selection.md'
14
+ - 'TanStack/table:packages/table-core/src/features/row-selection'
15
+ - 'TanStack/table:examples/react/row-selection'
25
16
  ---
26
17
 
27
- This skill builds on `tanstack-table/state-management` and `tanstack-table/column-definitions`. Read those first for state ownership and `getRowId`.
18
+ This skill builds on `core` and `table-features`. Selection is independent ID state; selected row models can only materialize loaded rows.
28
19
 
29
20
  ## Setup
30
21
 
31
22
  ```ts
32
- import {
33
- tableFeatures,
34
- rowSelectionFeature,
35
- constructTable,
36
- } from '@tanstack/table-core'
37
- import type { RowSelectionState } from '@tanstack/table-core'
23
+ import { rowSelectionFeature, tableFeatures } from '@tanstack/table-core'
38
24
 
39
- const features = tableFeatures({ rowSelectionFeature })
40
-
41
- const table = constructTable({
42
- features,
43
- columns,
44
- data,
45
- getRowId: (row) => row.id, // ← essentially mandatory
46
- initialState: { rowSelection: {} satisfies RowSelectionState },
47
- enableRowSelection: true,
48
- })
25
+ type Person = { id: string; name: string }
26
+ export const features = tableFeatures({ rowSelectionFeature })
27
+ export const options = {
28
+ getRowId: (row: Person) => row.id,
29
+ enableSubRowSelection: false,
30
+ }
49
31
  ```
50
32
 
51
33
  ## Core Patterns
52
34
 
53
- ### Select column with header "select all" + per-row checkbox
54
-
55
- ```tsx
56
- // From examples/react/row-selection/src/main.tsx
57
- columnHelper.display({
58
- id: 'select',
59
- header: ({ table }) => (
60
- <Checkbox
61
- checked={table.getIsAllRowsSelected()}
62
- indeterminate={table.getIsSomeRowsSelected()}
63
- onChange={table.getToggleAllRowsSelectedHandler()}
64
- />
65
- ),
66
- cell: ({ row }) => (
67
- <Checkbox
68
- checked={row.getIsSelected()}
69
- indeterminate={row.getIsSomeSelected()}
70
- disabled={!row.getCanSelect()}
71
- onChange={row.getToggleSelectedHandler()}
72
- />
73
- ),
74
- }),
75
- ```
76
-
77
- ### Single-select (radio-style)
78
-
79
- ```tsx
80
- const table = useTable({
81
- features: tableFeatures({ rowSelectionFeature }),
82
- columns,
83
- data,
84
- getRowId: (row) => row.id,
85
- enableMultiRowSelection: false, // ← radio-like
86
- })
87
-
88
- // Cell renders a radio, no "select all" header makes sense
89
- columnHelper.display({
90
- id: 'select',
91
- header: '', // no select-all in single-select mode
92
- cell: ({ row }) => (
93
- <input
94
- type="radio"
95
- name="row-selection"
96
- checked={row.getIsSelected()}
97
- disabled={!row.getCanSelect()}
98
- onChange={row.getToggleSelectedHandler()}
99
- />
100
- ),
101
- })
102
- ```
103
-
104
- ### Conditional selection per row
105
-
106
35
  ```ts
107
- const table = useTable({
108
- features,
109
- columns,
110
- data,
111
- getRowId: (row) => row.id,
112
- enableRowSelection: (row) => row.original.age > 18, // predicate form
113
- })
36
+ const selectedIds = table.getSelectedRowIds()
37
+ const loadedSelectedRows = table.getSelectedRowModel().rows
114
38
  ```
115
39
 
116
- ### Three selected-row APIs
117
-
118
- ```ts
119
- table.getSelectedRowModel() // built off core — raw data
120
- table.getFilteredSelectedRowModel() // built off filtered — current filters applied
121
- table.getGroupedSelectedRowModel() // built off grouped — current groups applied
122
- ```
123
-
124
- ### Hoist selection to an external atom
125
-
126
- ```tsx
127
- import { useCreateAtom } from '@tanstack/react-store'
128
-
129
- const rowSelectionAtom = useCreateAtom<RowSelectionState>({})
130
-
131
- const table = useTable({
132
- features,
133
- columns,
134
- data,
135
- getRowId: (row) => row.id,
136
- atoms: { rowSelection: rowSelectionAtom },
137
- })
138
-
139
- // Send selected IDs to an API call from a sibling component
140
- function ExportButton() {
141
- const selection = useStore(rowSelectionAtom)
142
- return (
143
- <button onClick={() => api.export(Object.keys(selection))}>Export</button>
144
- )
145
- }
146
- ```
40
+ Use IDs for database-wide intent and row models for currently loaded objects.
147
41
 
148
42
  ## Common Mistakes
149
43
 
150
- ### [HIGH] Omitting `getRowId` under `manualPagination`
44
+ ### [HIGH] Expecting selection to clean itself
151
45
 
152
- Wrong:
46
+ Wrong: `data = data.filter(row => row.id !== deletedId)`
153
47
 
154
- ```ts
155
- // Server-side pagination + default row.id = row.index
156
- const table = useTable({
157
- features: tableFeatures({ rowSelectionFeature, rowPaginationFeature }),
158
- data, // only current page from server
159
- manualPagination: true,
160
- rowCount,
161
- })
162
- // After paging, rowSelection: { '5': true } is ambiguous
163
- // — selection appears to move with the user.
164
- ```
48
+ Correct: `data = data.filter(row => row.id !== deletedId); table.setRowSelection(old => { const next = { ...old }; delete next[deletedId]; return next })`
165
49
 
166
- Correct:
50
+ Selection is independent state and can retain IDs after data removal.
167
51
 
168
- ```ts
169
- const table = useTable({
170
- features: tableFeatures({ rowSelectionFeature, rowPaginationFeature }),
171
- data,
172
- manualPagination: true,
173
- rowCount,
174
- getRowId: (row) => row.uuid, // stable across pages
175
- })
52
+ Source: `https://github.com/TanStack/table/issues/5850`
176
53
 
177
- // For "X of Y selected" with server-side pagination, read state directly:
178
- const totalSelected = Object.keys(table.state.rowSelection).length
179
- ```
180
-
181
- `row.id` defaults to `row.index`. Under `manualPagination`, every page reuses indices 0..n-1, so selection IDs collide across pages.
182
-
183
- Source: docs/guide/row-selection.md; examples/react/row-selection/src/main.tsx
184
-
185
- ### [MEDIUM] `enableMultiRowSelection: false` + a "select all" checkbox header
186
-
187
- Wrong:
188
-
189
- ```tsx
190
- const table = useTable({
191
- features: tableFeatures({ rowSelectionFeature }),
192
- enableMultiRowSelection: false, // radio-like
193
- })
194
-
195
- // Header still renders a checkbox + indeterminate
196
- <Checkbox
197
- checked={table.getIsAllRowsSelected()}
198
- indeterminate={table.getIsSomeRowsSelected()}
199
- onChange={table.getToggleAllRowsSelectedHandler()}
200
- />
201
- ```
202
-
203
- Correct:
204
-
205
- ```tsx
206
- const table = useTable({
207
- features: tableFeatures({ rowSelectionFeature }),
208
- enableMultiRowSelection: false,
209
- getRowId: (row) => row.id,
210
- })
211
-
212
- // Drop the toggle-all header in single-select mode
213
- columnHelper.display({
214
- id: 'select',
215
- header: '',
216
- cell: ({ row }) => (
217
- <input
218
- type="radio"
219
- name="row-selection"
220
- checked={row.getIsSelected()}
221
- disabled={!row.getCanSelect()}
222
- onChange={row.getToggleSelectedHandler()}
223
- />
224
- ),
225
- })
226
- ```
227
-
228
- In single-select mode, `mutateRowIsSelected` clears all other ids before adding the new one. "Select all" becomes effectively no-op and indeterminate is meaningless.
229
-
230
- Source: docs/guide/row-selection.md; packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
54
+ ### [HIGH] Selecting mutable indexes
231
55
 
232
- ### [HIGH] `getSelectedRowModel().flatRows` for counts under `manualPagination`
56
+ Wrong: `const options = { getRowId: (_row: Person, index: number) => String(index) }`
233
57
 
234
- Wrong:
58
+ Correct: `const options = { getRowId: (row: Person) => row.id }`
235
59
 
236
- ```ts
237
- // Under manualPagination, only counts the visible page's selected rows
238
- const selectedCount = table.getSelectedRowModel().flatRows.length
60
+ Stable application IDs preserve identity as row order and pages change.
239
61
 
240
- const handleBulkAction = () => {
241
- const ids = table.getSelectedRowModel().flatRows.map((row) => row.original.id)
242
- api.archive(ids) // missing all selections from other pages!
243
- }
244
- ```
62
+ Source: `docs/framework/react/guide/row-selection.md#useful-row-ids`
245
63
 
246
- Correct:
247
-
248
- ```ts
249
- // For counts and id lists under manualPagination, read state directly
250
- const selectedCount = Object.keys(table.state.rowSelection).length
251
-
252
- const handleBulkAction = () => {
253
- const ids = Object.keys(table.state.rowSelection)
254
- api.archive(ids)
255
- }
256
- // (Client-side: getSelectedRowModel is fine — data contains every row.)
257
- ```
64
+ ### [HIGH] Treating loaded model as global selection
258
65
 
259
- `getSelectedRowModel` walks the core row model — which under `manualPagination` only contains the current page. `state.rowSelection` may contain ids that aren't in `data` (that's by design).
66
+ Wrong: `const allSelectedRecords = table.getSelectedRowModel().rows`
260
67
 
261
- Source: docs/guide/row-selection.md; packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
68
+ Correct: `const allSelectedIds = table.getSelectedRowIds()`
262
69
 
263
- ### [MEDIUM] Surprise sub-row propagation from `enableSubRowSelection` default
264
-
265
- Wrong:
266
-
267
- ```ts
268
- // Default behavior: clicking the parent selects all children
269
- const table = useTable({
270
- features: tableFeatures({ rowSelectionFeature, rowExpandingFeature }),
271
- getSubRows: (row) => row.subRows,
272
- // enableSubRowSelection unset — defaults to true
273
- })
274
- ```
275
-
276
- Correct:
277
-
278
- ```ts
279
- const table = useTable({
280
- features: tableFeatures({ rowSelectionFeature, rowExpandingFeature }),
281
- getSubRows: (row) => row.subRows,
282
- enableSubRowSelection: false, // toggling parent doesn't touch subRows
283
- })
284
-
285
- // Or selectively:
286
- enableSubRowSelection: (row) => row.depth > 0,
287
-
288
- // Indeterminate parent checkbox
289
- <Checkbox
290
- checked={row.getIsSelected()}
291
- indeterminate={row.getIsSomeSelected()} // 'some' descendants selected
292
- disabled={!row.getCanSelect()}
293
- onChange={row.getToggleSelectedHandler()}
294
- />
295
- ```
296
-
297
- `enableSubRowSelection: true` is the default. `mutateRowIsSelected` recurses into `row.subRows` when truthy. Decide deliberately — "select group as a whole" UX wants this off.
298
-
299
- Source: docs/guide/row-selection.md; packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
300
-
301
- ### [HIGH] Parent checkbox stuck "unchecked" with all sub-rows selected (deep trees)
302
-
303
- Wrong:
304
-
305
- ```ts
306
- // Returns false even when all leaf descendants are selected
307
- const isParentChecked = row.getIsAllSubRowsSelected()
308
- ```
309
-
310
- Correct:
311
-
312
- ```ts
313
- const allLeafs = row.getLeafRows()
314
- const allSelected =
315
- allLeafs.length > 0 && allLeafs.every((r) => r.getIsSelected())
316
- const someSelected = allLeafs.some((r) => r.getIsSelected())
317
- ```
318
-
319
- `getIsAllSubRowsSelected` only counts direct children. With multi-level grouping, a parent reports based on a partial count.
320
-
321
- Source: https://github.com/TanStack/table/issues/4878; https://github.com/TanStack/table/issues/4759
322
-
323
- ### [HIGH] Stale rowSelection IDs after data refresh
324
-
325
- Wrong:
326
-
327
- ```ts
328
- useEffect(() => {
329
- refreshData() // selection still references deleted IDs
330
- }, [trigger])
331
- ```
332
-
333
- Correct:
334
-
335
- ```ts
336
- useEffect(() => {
337
- setRowSelection((prev) => {
338
- const validIds = new Set(data.map((row) => row.id))
339
- const next: RowSelectionState = {}
340
- for (const id in prev) if (validIds.has(id)) next[id] = prev[id]
341
- return next
342
- })
343
- }, [data])
344
- ```
345
-
346
- v8 removed v7's `autoResetSelectedRows`. With websockets / refetch, IDs that no longer exist remain in `rowSelection` and `getIsAllRowsSelected()` returns true based on stale state. Prune yourself.
347
-
348
- Source: https://github.com/TanStack/table/issues/5850; https://github.com/TanStack/table/issues/4498
349
-
350
- ### [CRITICAL] Reimplementing selection state manually
351
-
352
- Wrong:
353
-
354
- ```ts
355
- // Hand-rolled "selected" set, bypassing the table
356
- const [selected, setSelected] = useState(new Set<string>())
357
- const toggle = (id: string) => {
358
- setSelected((s) => {
359
- const next = new Set(s)
360
- next.has(id) ? next.delete(id) : next.add(id)
361
- return next
362
- })
363
- }
364
- ```
365
-
366
- Correct:
367
-
368
- ```ts
369
- const table = useTable({
370
- features: tableFeatures({ rowSelectionFeature }),
371
- columns,
372
- data,
373
- getRowId: (row) => row.id,
374
- })
375
- row.toggleSelected()
376
- row.toggleSelected(true)
377
- table.toggleAllRowsSelected()
378
- table.setRowSelection({ abc: true })
379
- ```
70
+ Under manual pagination, unloaded selected IDs have no `Row` object in the current model.
380
71
 
381
- Source: maintainer interview (Phase 4, 2026-05-17)
72
+ Source: `docs/framework/react/guide/row-selection.md#note-if-you-are-using-manualpagination`
382
73
 
383
- ## See also
74
+ ## API Discovery
384
75
 
385
- - `tanstack-table/column-definitions` `getRowId` is the foundation of every row-keyed feature
386
- - `tanstack-table/state-management` — `rowSelection` slice + atoms for sharing selection
387
- - `tanstack-table/pagination` — server-pagination "select all" pitfalls
388
- - `tanstack-table/grouping` — `getGroupedSelectedRowModel` distinction
76
+ Inspect `node_modules/@tanstack/table-core/src/features/row-selection/` for state, row-model variants, and selection enablement callbacks.