@tanstack/vue-table 9.0.0-beta.6 → 9.0.0-beta.62

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 (61) hide show
  1. package/README.md +2 -0
  2. package/dist/FlexRender.d.ts +1 -2
  3. package/dist/FlexRender.js +5 -5
  4. package/dist/createTableHook.d.ts +39 -13
  5. package/dist/createTableHook.js +8 -23
  6. package/dist/experimental-worker-plugin.d.ts +1 -0
  7. package/dist/experimental-worker-plugin.js +3 -0
  8. package/dist/index.d.ts +2 -2
  9. package/dist/merge-proxy.js +1 -2
  10. package/dist/reactivity.js +3 -8
  11. package/dist/useTable.d.ts +2 -10
  12. package/dist/useTable.js +4 -9
  13. package/package.json +12 -20
  14. package/skills/create-table-hook/SKILL.md +146 -0
  15. package/skills/getting-started/SKILL.md +144 -0
  16. package/skills/migrate-v8-to-v9/SKILL.md +185 -0
  17. package/skills/table-state/SKILL.md +191 -0
  18. package/skills/with-tanstack-query/SKILL.md +127 -0
  19. package/skills/with-tanstack-virtual/SKILL.md +116 -0
  20. package/dist/FlexRender.cjs +0 -80
  21. package/dist/FlexRender.cjs.map +0 -1
  22. package/dist/FlexRender.d.cts +0 -63
  23. package/dist/FlexRender.js.map +0 -1
  24. package/dist/createTableHook.cjs +0 -194
  25. package/dist/createTableHook.cjs.map +0 -1
  26. package/dist/createTableHook.d.cts +0 -135
  27. package/dist/createTableHook.js.map +0 -1
  28. package/dist/flex-render.cjs +0 -5
  29. package/dist/flex-render.d.cts +0 -2
  30. package/dist/index.cjs +0 -17
  31. package/dist/index.d.cts +0 -5
  32. package/dist/merge-proxy.cjs +0 -77
  33. package/dist/merge-proxy.cjs.map +0 -1
  34. package/dist/merge-proxy.js.map +0 -1
  35. package/dist/reactivity.cjs +0 -64
  36. package/dist/reactivity.cjs.map +0 -1
  37. package/dist/reactivity.js.map +0 -1
  38. package/dist/static-functions.cjs +0 -9
  39. package/dist/static-functions.d.cts +0 -1
  40. package/dist/useTable.cjs +0 -76
  41. package/dist/useTable.cjs.map +0 -1
  42. package/dist/useTable.d.cts +0 -43
  43. package/dist/useTable.js.map +0 -1
  44. package/skills/vue/client-to-server/SKILL.md +0 -365
  45. package/skills/vue/compose-with-tanstack-form/SKILL.md +0 -369
  46. package/skills/vue/compose-with-tanstack-pacer/SKILL.md +0 -318
  47. package/skills/vue/compose-with-tanstack-query/SKILL.md +0 -385
  48. package/skills/vue/compose-with-tanstack-store/SKILL.md +0 -301
  49. package/skills/vue/compose-with-tanstack-virtual/SKILL.md +0 -340
  50. package/skills/vue/getting-started/SKILL.md +0 -409
  51. package/skills/vue/migrate-v8-to-v9/SKILL.md +0 -375
  52. package/skills/vue/production-readiness/SKILL.md +0 -271
  53. package/skills/vue/table-state/SKILL.md +0 -403
  54. package/src/FlexRender.ts +0 -138
  55. package/src/createTableHook.ts +0 -534
  56. package/src/flex-render.ts +0 -1
  57. package/src/index.ts +0 -4
  58. package/src/merge-proxy.ts +0 -132
  59. package/src/reactivity.ts +0 -89
  60. package/src/static-functions.ts +0 -1
  61. package/src/useTable.ts +0 -181
@@ -1,375 +0,0 @@
1
- ---
2
- name: vue/migrate-v8-to-v9
3
- description: >
4
- Mechanical breaking-change migration from `@tanstack/vue-table` v8 to v9. Rename `useVueTable`
5
- → `useTable`, move `getCoreRowModel`/`getSortedRowModel`/etc. options into `rowModels`
6
- factories, add the mandatory `features` via `tableFeatures({...})`, update
7
- `createColumnHelper<TData>()` → `createColumnHelper<typeof features, TData>()`, rename
8
- `sortingFn`/`sortingFns` → `sortFn`/`sortFns`, swap `table.getState()` for `table.state`
9
- / `table.state` / `table.atoms.<slice>.get()`, and prefer `<FlexRender :cell="cell" />` over the
10
- legacy `:render`/`:props` shape. Vue has NO `/legacy` entrypoint — migration is a direct
11
- rewrite. The Vue adapter installs `vueReactivity()` automatically.
12
- type: lifecycle
13
- library: tanstack-table
14
- framework: vue
15
- library_version: '9.0.0-alpha.48'
16
- requires:
17
- - setup
18
- - state-management
19
- - column-definitions
20
- sources:
21
- - docs/framework/vue/guide/migrating.md
22
- - docs/framework/vue/vue-table.md
23
- - docs/framework/vue/guide/table-state.md
24
- - packages/vue-table/src/useTable.ts
25
- - packages/vue-table/src/FlexRender.ts
26
- ---
27
-
28
- # Migrate @tanstack/vue-table v8 → v9
29
-
30
- ## Dependencies
31
-
32
- ```bash
33
- pnpm add @tanstack/vue-table@latest
34
- # Optional, for external atoms during/after migration:
35
- pnpm add @tanstack/vue-store
36
- ```
37
-
38
- **Vue has no `/legacy` entrypoint.** The `useLegacyTable` React shim is React-only — Vue
39
- projects rewrite each table directly. The good news: most call sites are mechanical renames.
40
-
41
- ## Setup — the v8 vs v9 shape side-by-side
42
-
43
- ### v8 (before)
44
-
45
- ```vue
46
- <script setup lang="ts">
47
- import {
48
- FlexRender,
49
- createColumnHelper,
50
- getCoreRowModel,
51
- getSortedRowModel,
52
- getPaginationRowModel,
53
- useVueTable,
54
- } from '@tanstack/vue-table'
55
- import type { ColumnDef } from '@tanstack/vue-table'
56
-
57
- const columnHelper = createColumnHelper<Person>()
58
- const columns: ColumnDef<Person, any>[] = [
59
- columnHelper.accessor('age', { header: 'Age', sortingFn: 'alphanumeric' }),
60
- ]
61
-
62
- const table = useVueTable({
63
- columns,
64
- data,
65
- getCoreRowModel: getCoreRowModel(),
66
- getSortedRowModel: getSortedRowModel(),
67
- getPaginationRowModel: getPaginationRowModel(),
68
- })
69
- </script>
70
-
71
- <template>
72
- <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
73
- </template>
74
- ```
75
-
76
- ### v9 (after)
77
-
78
- ```vue
79
- <script setup lang="ts">
80
- import {
81
- FlexRender,
82
- createColumnHelper,
83
- createPaginatedRowModel,
84
- createSortedRowModel,
85
- rowPaginationFeature,
86
- rowSortingFeature,
87
- sortFns,
88
- tableFeatures,
89
- useTable,
90
- } from '@tanstack/vue-table'
91
-
92
- const features = tableFeatures({ rowSortingFeature, rowPaginationFeature })
93
- const columnHelper = createColumnHelper<typeof features, Person>()
94
- const columns = columnHelper.columns([
95
- columnHelper.accessor('age', { header: 'Age', sortFn: 'alphanumeric' }), // sortingFn → sortFn
96
- ])
97
-
98
- const table = useTable({
99
- features,
100
- rowModels: {
101
- sortedRowModel: createSortedRowModel(sortFns),
102
- paginatedRowModel: createPaginatedRowModel(),
103
- },
104
- columns,
105
- data,
106
- })
107
- </script>
108
-
109
- <template>
110
- <!-- Preferred shorthand. Legacy :render / :props still compiles. -->
111
- <FlexRender :cell="cell" />
112
- </template>
113
- ```
114
-
115
- ## Rename Cheat-Sheet
116
-
117
- | v8 | v9 |
118
- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
119
- | `useVueTable(opts)` | `useTable(opts, selector?)` |
120
- | `getCoreRowModel: getCoreRowModel()` | implicit; not an option |
121
- | `getSortedRowModel: getSortedRowModel()` | `rowModels: { sortedRowModel: createSortedRowModel(sortFns) }` |
122
- | `getFilteredRowModel: getFilteredRowModel()` | `rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }` |
123
- | `getPaginationRowModel: getPaginationRowModel()` | `rowModels: { paginatedRowModel: createPaginatedRowModel() }` |
124
- | `getGroupedRowModel: getGroupedRowModel()` | `rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }` |
125
- | `createColumnHelper<TData>()` | `createColumnHelper<typeof features, TData>()` |
126
- | `ColumnDef<TData, TValue>` | `ColumnDef<TFeatures, TData, TValue>` |
127
- | `Column<TData, TValue>` / `Row<TData>` / `Cell<TData, TValue>` | `…<TFeatures, TData, TValue>` |
128
- | `sortingFn` (column def) | `sortFn` |
129
- | `sortingFns` | `sortFns` |
130
- | `getSortingFn()` / `getAutoSortingFn()` | `getSortFn()` / `getAutoSortFn()` |
131
- | `SortingFn` / `SortingFns` types | `SortFn` / `SortFns` |
132
- | `enablePinning: true` | `enableColumnPinning` and/or `enableRowPinning` |
133
- | `state.columnSizingInfo` | `state.columnResizing` |
134
- | `onColumnSizingInfoChange` | `onColumnResizingChange` |
135
- | `ColumnSizing` feature | `columnSizingFeature` + `columnResizingFeature` (split) |
136
- | `table.getState()` | `table.state` (full) / `table.state` (selector) / `table.atoms.<slice>.get()` |
137
- | `row._getAllCellsByColumnId()` | `row.getAllCellsByColumnId()` (underscore removed) |
138
- | `table._getFacetedRowModel()` / `_getFacetedMinMaxValues()` / `_getFacetedUniqueValues()` | Same names without leading underscore |
139
- | `<FlexRender :render="…" :props="…" />` | `<FlexRender :cell="cell" />` / `:header` / `:footer` (preferred; legacy still works) |
140
-
141
- Source: `docs/framework/vue/guide/migrating.md`, `docs/framework/react/guide/migrating.md` (the
142
- non-Vue-specific renames are shared across adapters).
143
-
144
- ## Core Patterns
145
-
146
- ### 1. Convert `getXRowModel` options to `rowModels` factories
147
-
148
- Every row-model factory now requires its `*Fns` parameter — this is what makes the registries
149
- tree-shakeable in v9.
150
-
151
- ```ts
152
- rowModels: {
153
- sortedRowModel: createSortedRowModel(sortFns),
154
- filteredRowModel: createFilteredRowModel(filterFns),
155
- paginatedRowModel: createPaginatedRowModel(),
156
- groupedRowModel: createGroupedRowModel(aggregationFns),
157
- expandedRowModel: createExpandedRowModel(),
158
- }
159
- ```
160
-
161
- The `*Fns` registries are open-ended; do not cite a number of built-in fns.
162
-
163
- ### 2. Add `tableFeatures` and register every feature you use
164
-
165
- If a feature isn't in `features`, its API isn't on the table (TS error AND runtime
166
- `undefined`). This is v9's biggest behavioral change.
167
-
168
- ```ts
169
- import {
170
- columnFilteringFeature,
171
- rowPaginationFeature,
172
- rowSortingFeature,
173
- rowSelectionFeature,
174
- tableFeatures,
175
- } from '@tanstack/vue-table'
176
-
177
- const features = tableFeatures({
178
- rowSortingFeature,
179
- rowPaginationFeature,
180
- columnFilteringFeature,
181
- rowSelectionFeature,
182
- })
183
- ```
184
-
185
- For a quick migration that doesn't audit usage, `stockFeatures` is the "everything" escape
186
- hatch — but it forfeits the bundle benefit. Prefer explicit registration.
187
-
188
- ### 3. Move state reads off `table.getState()`
189
-
190
- ```ts
191
- // v8
192
- const sorting = table.getState().sorting
193
-
194
- // v9 — pick the narrowest read.
195
- const sorting = table.atoms.sorting.get() // narrowest, no full state object built
196
- const snapshot = table.state // full readonly view
197
- const table = useTable(opts, (s) => ({ sorting: s.sorting })) // selected reactive state
198
- table.state.sorting // typed selector output
199
- ```
200
-
201
- For Vue reactivity, wrap an atom read in `computed`:
202
-
203
- ```ts
204
- const sorting = computed(() => table.atoms.sorting.get())
205
- ```
206
-
207
- ### 4. v8 controlled state still works — just use getters
208
-
209
- v8 controlled state via `state` + `on[State]Change` is preserved in v9 for migration paths.
210
- The Vue-specific rule is that each `state.<slice>` must be a **getter** so Vue tracks `.value`.
211
-
212
- ```ts
213
- const sorting = ref<SortingState>([])
214
-
215
- const table = useTable({
216
- features,
217
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
218
- columns,
219
- data,
220
- state: {
221
- get sorting() {
222
- return sorting.value
223
- }, // ← getter, not raw ref
224
- },
225
- onSortingChange: (u) => {
226
- sorting.value = typeof u === 'function' ? u(sorting.value) : u
227
- },
228
- })
229
- ```
230
-
231
- For new code, prefer `atoms: { sorting: sortingAtom }` from `@tanstack/vue-store` — no
232
- `on[State]Change` plumbing required. See `tanstack-table/vue/compose-with-tanstack-store`.
233
-
234
- ### 5. Update `<FlexRender>` shape
235
-
236
- ```vue
237
- <!-- v8 (still works in v9, but verbose) -->
238
- <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
239
-
240
- <!-- v9 preferred -->
241
- <FlexRender :cell="cell" />
242
- <FlexRender :header="header" />
243
- <FlexRender :footer="header" />
244
- ```
245
-
246
- Source: `packages/vue-table/src/FlexRender.ts`.
247
-
248
- ## Common Mistakes
249
-
250
- ### Importing `useVueTable` (CRITICAL)
251
-
252
- ```ts
253
- // ❌
254
- import { useVueTable } from '@tanstack/vue-table'
255
-
256
- // ✅
257
- import { useTable } from '@tanstack/vue-table'
258
- ```
259
-
260
- `useVueTable` is not exported. Migration is a rename.
261
-
262
- ### Trying to use `useLegacyTable` in Vue (CRITICAL)
263
-
264
- `useLegacyTable` is a React-only shim from `@tanstack/react-table/legacy`. There is no Vue
265
- equivalent. If you have many tables to convert, do them one file at a time — the renames are
266
- mechanical.
267
-
268
- ### Passing `getCoreRowModel: getCoreRowModel()` as an option (CRITICAL)
269
-
270
- ```ts
271
- // ❌ v8 muscle memory.
272
- const table = useTable({
273
- features,
274
- columns,
275
- data,
276
- getCoreRowModel: getCoreRowModel(),
277
- })
278
-
279
- // ✅ Core row model is implicit. `rowModels: {}` is fine.
280
- const table = useTable({ features, rowModels: {}, columns, data })
281
- ```
282
-
283
- ### Forgetting `features` (CRITICAL)
284
-
285
- `features` is required even for a no-features migration. Pass `tableFeatures({})` for empty,
286
- or list everything you use. Without it: `'features' is missing in type`.
287
-
288
- ### Wrong `createColumnHelper` generic arity (CRITICAL)
289
-
290
- ```ts
291
- // ❌ v8
292
- const columnHelper = createColumnHelper<Person>()
293
-
294
- // ✅ v9
295
- const columnHelper = createColumnHelper<typeof features, Person>()
296
- ```
297
-
298
- Same applies to type annotations: `ColumnDef<typeof features, Person>`,
299
- `Row<typeof features, Person>`, `Cell<typeof features, Person, unknown>`.
300
-
301
- ### Forgetting to pass `sortFns` / `filterFns` to row-model factories (CRITICAL)
302
-
303
- ```ts
304
- // ❌ Runtime: no sort fns registered, sort is a no-op.
305
- sortedRowModel: createSortedRowModel()
306
-
307
- // ✅
308
- sortedRowModel: createSortedRowModel(sortFns)
309
- ```
310
-
311
- ### Missed `sortingFn` → `sortFn` rename (HIGH)
312
-
313
- ```ts
314
- // ❌ v8 name on a column def. TypeScript will complain.
315
- columnHelper.accessor('age', { header: 'Age', sortingFn: 'alphanumeric' })
316
-
317
- // ✅
318
- columnHelper.accessor('age', { header: 'Age', sortFn: 'alphanumeric' })
319
- ```
320
-
321
- ### `enablePinning: true` (HIGH)
322
-
323
- Split in v9 — pick one or both:
324
-
325
- ```ts
326
- useTable({
327
- // ...
328
- enableColumnPinning: true,
329
- enableRowPinning: true,
330
- })
331
- ```
332
-
333
- ### Reading `table.getState()` everywhere (HIGH)
334
-
335
- The method was removed. Replace each call site explicitly — the right substitute depends on
336
- the read pattern (see "Move state reads off `table.getState()`" above).
337
-
338
- ### Underscore-prefixed APIs (HIGH)
339
-
340
- ```ts
341
- row._getAllCellsByColumnId() // ❌
342
- row.getAllCellsByColumnId() // ✅
343
-
344
- table._getFacetedRowModel() // ❌
345
- table.getFacetedRowModel() // ✅
346
- ```
347
-
348
- Drop the underscore on every former internal API.
349
-
350
- ### Bundling `stockFeatures` and calling it done (HIGH)
351
-
352
- `stockFeatures` is the "ship everything" escape hatch — useful during the rename pass to keep
353
- things compiling, but if you don't go back and replace it with an explicit `tableFeatures({...})`
354
- that lists only what you actually render, you forfeit v9's tree-shaking — which is one of the
355
- main reasons to migrate.
356
-
357
- ### Hallucinating pre-v9 names (CRITICAL — top AI tell)
358
-
359
- `useTable` from `react-table` v7, `useVueTable` from v8, `getCoreRowModel()` as an option,
360
- single-generic `createColumnHelper<TData>()`, `sortingFn`, `enablePinning`, `table.getState()` —
361
- all v7/v8 shapes. None of them compile in v9.
362
-
363
- ### Reimplementing what built-in APIs already provide (CRITICAL — #1 AI tell)
364
-
365
- Migration is a great time to delete hand-rolled sort/filter/select state machines and route
366
- through `table.setSorting`, `table.setColumnFilters`, `row.toggleSelected`, `table.nextPage`,
367
- etc.
368
-
369
- ## See Also
370
-
371
- - `tanstack-table/vue/getting-started` — the v9 minimum-viable shape
372
- - `tanstack-table/vue/table-state` — reactivity model + reading state
373
- - `tanstack-table/vue/production-readiness` — finish the migration with bundle + identity audits
374
- - `tanstack-table/table-core/setup` — `features` / `rowModels` deep dive
375
- - `tanstack-table/table-core/column-definitions` — column helper + generics
@@ -1,271 +0,0 @@
1
- ---
2
- name: vue/production-readiness
3
- description: >
4
- Ship-ready optimizations for `@tanstack/vue-table` v9. Tree-shake by listing ONLY the
5
- `features` you actually render — never default to `stockFeatures`. Keep `features`,
6
- `rowModels`, `columnHelper`, and `columns` at module scope for stable identity (Vue
7
- re-evaluates the `<script setup>` block per component instance — declare these outside or in
8
- a const). Use the `useTable(opts, selector)` second argument or per-slice `computed(() =>
9
- table.atoms.<slice>.get())` / `useSelector(table.atoms.<slice>)` from `@tanstack/vue-store`
10
- to narrow re-renders. Vue's reactivity already evaluates only dirty computed deps, so
11
- `table.Subscribe` is NOT the React-Compiler workaround it is in React — reach for
12
- fine-grained reads (`computed`, `useSelector`) first.
13
- type: lifecycle
14
- library: tanstack-table
15
- framework: vue
16
- library_version: '9.0.0-alpha.48'
17
- requires:
18
- - setup
19
- - state-management
20
- - vue/table-state
21
- sources:
22
- - docs/guide/features.md
23
- - docs/framework/vue/guide/table-state.md
24
- - packages/vue-table/src/useTable.ts
25
- - packages/vue-table/src/reactivity.ts
26
- ---
27
-
28
- # Production Readiness (Vue)
29
-
30
- ## Dependencies
31
-
32
- ```bash
33
- pnpm add @tanstack/vue-table @tanstack/vue-store
34
- ```
35
-
36
- `@tanstack/vue-store` is required if you want to use `useSelector` for per-slice atom
37
- subscriptions outside the table.
38
-
39
- ## Setup — the production-ready shape
40
-
41
- ```ts
42
- // table-setup.ts — module scope, stable identity for everyone.
43
- import {
44
- createPaginatedRowModel,
45
- createSortedRowModel,
46
- rowPaginationFeature,
47
- rowSortingFeature,
48
- sortFns,
49
- tableFeatures,
50
- } from '@tanstack/vue-table'
51
-
52
- export const features = tableFeatures({
53
- rowSortingFeature,
54
- rowPaginationFeature,
55
- })
56
-
57
- export const rowModels = {
58
- sortedRowModel: createSortedRowModel(sortFns),
59
- paginatedRowModel: createPaginatedRowModel(),
60
- }
61
- ```
62
-
63
- ```vue
64
- <script setup lang="ts">
65
- import { ref } from 'vue'
66
- import { useTable } from '@tanstack/vue-table'
67
- import { features, rowModels } from './table-setup'
68
- import { columns } from './columns'
69
-
70
- const props = defineProps<{ data: Person[] }>()
71
- const data = ref(props.data)
72
-
73
- // Narrow selector — table.state only carries what we actually render at this level.
74
- const table = useTable({ features, rowModels, columns, data }, (state) => ({
75
- pagination: state.pagination,
76
- sorting: state.sorting,
77
- }))
78
- </script>
79
- ```
80
-
81
- ## Core Patterns
82
-
83
- ### 1. Tree-shake by listing only the features you render
84
-
85
- ```ts
86
- // ❌ Ships filtering, faceting, grouping, pinning, expanding, sizing, resizing,
87
- // visibility, ordering, row-selection, row-pinning code you don't use.
88
- const features = tableFeatures(stockFeatures)
89
-
90
- // ✅ A sort-only table ships ~6–7 kb of feature code instead of ~15–20 kb.
91
- const features = tableFeatures({ rowSortingFeature })
92
- ```
93
-
94
- `stockFeatures` exists for migrations and prototyping. Audit before shipping — if a feature
95
- isn't wired into a column def or UI handler, drop it. v9 is tree-shakeable specifically so you
96
- only pay for what you use.
97
-
98
- ### 2. Module-scope `features` / `rowModels` / `columnHelper` / `columns`
99
-
100
- ```ts
101
- // ❌ Inside <script setup> — every component instance creates new identities, which churns
102
- // the table's option-watcher and triggers a `setOptions` on every reactive tick.
103
- const features = tableFeatures({ rowSortingFeature })
104
- const columnHelper = createColumnHelper<typeof features, Person>()
105
- const columns = columnHelper.columns([
106
- /* ... */
107
- ])
108
- ```
109
-
110
- ```ts
111
- // ✅ Module scope — one identity, shared across instances, GC-friendly.
112
- const features = tableFeatures({ rowSortingFeature })
113
- const columnHelper = createColumnHelper<typeof features, Person>()
114
- export const columns = columnHelper.columns([
115
- /* ... */
116
- ])
117
- ```
118
-
119
- `data` is the exception: a stable `ref` is fine — the adapter watches `.value`. Just don't
120
- write `data: ref([...])` inline in the table options (a fresh ref each render).
121
-
122
- ### 3. Narrow the `useTable` selector
123
-
124
- ```ts
125
- // ❌ Default selector — table.state contains every registered slice; any state change re-renders
126
- // every consumer of table.state.
127
- const table = useTable({ features, rowModels, columns, data })
128
-
129
- // ✅ Project only what THIS component renders. Other slices still drive table internals; you
130
- // just don't subscribe to them at the component level.
131
- const table = useTable({ features, rowModels, columns, data }, (state) => ({
132
- pagination: state.pagination,
133
- sorting: state.sorting,
134
- }))
135
- ```
136
-
137
- The selector is a TanStack Store selector — its compare is shallow by default, so identity
138
- stable returns avoid re-evaluation.
139
-
140
- ### 4. Per-slice subscriptions for nested components
141
-
142
- When only a leaf component cares about one slice, subscribe to the atom directly. This is
143
- narrower than `useTable`'s selector (which still touches the full store).
144
-
145
- ```vue
146
- <!-- SelectedCount.vue -->
147
- <script setup lang="ts">
148
- import { useSelector } from '@tanstack/vue-store'
149
- import type { TableProp } from './types'
150
-
151
- const props = defineProps<TableProp>()
152
- // Re-renders only when rowSelection changes.
153
- const selection = useSelector(props.table.atoms.rowSelection)
154
- </script>
155
-
156
- <template>
157
- <span>{{ Object.keys(selection).length }} selected</span>
158
- </template>
159
- ```
160
-
161
- Or with `computed` (no `vue-store` peer required, but only reactive in a Vue scope):
162
-
163
- ```ts
164
- const selectedCount = computed(
165
- () => Object.keys(table.atoms.rowSelection.get()).length,
166
- )
167
- ```
168
-
169
- ### 5. Vue does NOT need `table.Subscribe` the way React does
170
-
171
- `table.Subscribe` is a render-prop component primarily useful in React when the React Compiler
172
- can't see through the table closure. Vue's reactivity already re-evaluates dependent reads
173
- automatically — `computed`, `useSelector`, and the `useTable` selector cover the same ground
174
- with less indirection.
175
-
176
- Use `table.Subscribe` only for cross-store source subscription (`<table.Subscribe :source="..."
177
- :selector="..."`) — and even then, `useSelector(source, selector)` is usually cleaner.
178
-
179
- ### 6. Stress patterns
180
-
181
- - **Virtualize rows / columns** — pair with `@tanstack/vue-virtual`. Keep `useVirtualizer` in
182
- the deepest possible component (`TableBody`, not `App`) so unrelated state changes don't
183
- re-run it. See `tanstack-table/vue/compose-with-tanstack-virtual`.
184
- - **Debounce filter / resize writes** — see `tanstack-table/vue/compose-with-tanstack-pacer`.
185
- - **Hoist heavy column defs out of `<script setup>`** — into a `columns.ts` module.
186
-
187
- ## Common Mistakes
188
-
189
- ### Using `stockFeatures` in production without auditing (HIGH)
190
-
191
- Defeats the entire reason v9 is tree-shakeable. Replace with an explicit
192
- `tableFeatures({...})` listing only the features your UI renders.
193
-
194
- ### Re-declaring `features` / `rowModels` / `columns` inside `<script setup>` (HIGH)
195
-
196
- Vue runs `<script setup>` once per component instance, but that's still per-page-navigation in
197
- SPAs and per-instance in any reusable component. Module scope is cheapest and shared.
198
-
199
- ### Inline `data: ref([])` or `data: computed(() => something ?? [])` in options (MEDIUM)
200
-
201
- The `?? []` pattern creates a fresh array identity on every recompute. Either:
202
-
203
- ```ts
204
- // ✅ stable empty array
205
- const EMPTY: Person[] = []
206
- const data = computed(() => dataQuery.data.value?.rows ?? EMPTY)
207
-
208
- // ✅ or hold a ref that's only updated on real data changes
209
- const data = ref<Person[]>([])
210
- watchEffect(() => {
211
- const next = dataQuery.data.value?.rows
212
- if (next && next !== data.value) data.value = next
213
- })
214
- ```
215
-
216
- ### Leaving the default selector when only one slice is rendered (MEDIUM)
217
-
218
- ```ts
219
- // ❌ Whole table re-evaluates table.state on every keystroke in a column filter.
220
- const table = useTable({ features, rowModels, columns, data })
221
-
222
- // ✅
223
- const table = useTable(opts, (s) => ({ pagination: s.pagination }))
224
- ```
225
-
226
- ### Premature `table.Subscribe` everywhere (MEDIUM)
227
-
228
- For a 50-row table, wrapping every header in `<table.Subscribe>` adds complexity without
229
- measurable benefit. Vue's default reactivity is already fine-grained — reach for `Subscribe` /
230
- selectors only when you've measured a hotspot.
231
-
232
- Maintainer guidance: advanced state-management patterns are for advanced cases.
233
-
234
- ### Reading `table.state` in a deep component when only that component cares (MEDIUM)
235
-
236
- If only `SelectedCount.vue` cares about `rowSelection`, prefer
237
- `useSelector(table.atoms.rowSelection)` over reading `table.state.rowSelection` at a parent
238
- and prop-drilling.
239
-
240
- ### Hoisting `useTable` above a virtualizer (HIGH)
241
-
242
- If a parent component reads `table.state` reactively, every state change re-renders the
243
- parent, which re-runs `useVirtualizer` and blows away scroll position. Keep
244
- `useVirtualizer` inside the leaf component that needs it; pass `table` (which is stable) as a
245
- prop. See `tanstack-table/vue/compose-with-tanstack-virtual`.
246
-
247
- ### Hallucinating pre-v9 API names (CRITICAL)
248
-
249
- `stockFeatures` exists in v9; `useVueTable` does not; `getCoreRowModel()` is not a valid
250
- option in v9. If you're optimizing v8 code, you're optimizing the wrong codebase — see
251
- `tanstack-table/vue/migrate-v8-to-v9` first.
252
-
253
- ### "API missing" because feature not registered (CRITICAL — v9-specific)
254
-
255
- You can't tree-shake a feature you never added. If `table.setSorting` is `undefined`, the
256
- solution isn't a workaround — it's `tableFeatures({ rowSortingFeature })`. Optimization
257
- follows correctness.
258
-
259
- ### Reimplementing built-in transitions (CRITICAL — #1 AI tell)
260
-
261
- `table.setSorting`, `row.toggleSelected`, `table.nextPage`, `column.setFilterValue` — all
262
- exist for nearly every state transition. Hand-rolled state machines are slower (skip
263
- internal invariants) and harder to maintain (reset APIs, multi-sort, etc. don't apply).
264
-
265
- ## See Also
266
-
267
- - `tanstack-table/vue/table-state` — selector, atoms, FlexRender deep dive
268
- - `tanstack-table/vue/getting-started` — the simple shape you're now optimizing
269
- - `tanstack-table/vue/compose-with-tanstack-virtual` — virtualization for stress cases
270
- - `tanstack-table/vue/compose-with-tanstack-pacer` — debounce filter / throttle resize writes
271
- - `tanstack-table/vue/compose-with-tanstack-store` — external atoms in depth