@tanstack/vue-table 9.0.0-beta.40 → 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.
- package/README.md +1 -0
- package/package.json +2 -2
- package/skills/create-table-hook/SKILL.md +146 -0
- package/skills/getting-started/SKILL.md +144 -0
- package/skills/migrate-v8-to-v9/SKILL.md +185 -0
- package/skills/table-state/SKILL.md +191 -0
- package/skills/with-tanstack-query/SKILL.md +127 -0
- package/skills/with-tanstack-virtual/SKILL.md +116 -0
- package/skills/vue/client-to-server/SKILL.md +0 -360
- package/skills/vue/compose-with-tanstack-form/SKILL.md +0 -369
- package/skills/vue/compose-with-tanstack-pacer/SKILL.md +0 -321
- package/skills/vue/compose-with-tanstack-query/SKILL.md +0 -383
- package/skills/vue/compose-with-tanstack-store/SKILL.md +0 -302
- package/skills/vue/compose-with-tanstack-virtual/SKILL.md +0 -344
- package/skills/vue/getting-started/SKILL.md +0 -415
- package/skills/vue/migrate-v8-to-v9/SKILL.md +0 -393
- package/skills/vue/production-readiness/SKILL.md +0 -278
- package/skills/vue/table-state/SKILL.md +0 -399
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: vue/table-state
|
|
3
|
-
description: >
|
|
4
|
-
Vue reactivity for `@tanstack/vue-table` v9. Covers `useTable(options, selector?)`, reactive
|
|
5
|
-
`data`/`columns` via `ref`/`computed` getters, the `vueReactivity()` binding (readonly atoms →
|
|
6
|
-
`computed`, writable → `shallowRef`, subscriptions via `watch({flush:'sync'})`), the three state
|
|
7
|
-
surfaces `table.atoms.<slice>` / `table.store` / `table.state`, selected state via the second
|
|
8
|
-
`useTable` argument, the `<FlexRender>` component, `table.Subscribe` for atom/source subscriptions,
|
|
9
|
-
and composition with `createAtom` / `useSelector` from `@tanstack/vue-store`.
|
|
10
|
-
type: framework
|
|
11
|
-
library: tanstack-table
|
|
12
|
-
framework: vue
|
|
13
|
-
library_version: '9.0.0-alpha.48'
|
|
14
|
-
requires:
|
|
15
|
-
- state-management
|
|
16
|
-
- setup
|
|
17
|
-
sources:
|
|
18
|
-
- docs/framework/vue/guide/table-state.md
|
|
19
|
-
- docs/framework/vue/vue-table.md
|
|
20
|
-
- packages/vue-table/src/useTable.ts
|
|
21
|
-
- packages/vue-table/src/reactivity.ts
|
|
22
|
-
- packages/vue-table/src/FlexRender.ts
|
|
23
|
-
- examples/vue/basic-use-table/
|
|
24
|
-
- examples/vue/basic-external-atoms/
|
|
25
|
-
- examples/vue/basic-external-state/
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
# Vue Table State, Subscribe & createTableHook
|
|
29
|
-
|
|
30
|
-
## Dependencies
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
pnpm add @tanstack/vue-table @tanstack/vue-store
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
`@tanstack/vue-store` is a peer for `createAtom` / `useSelector`. It is only required when you
|
|
37
|
-
opt into external atoms — basic tables that use `initialState` or built-in state work without it.
|
|
38
|
-
|
|
39
|
-
This skill is v9-specific (`9.0.0-alpha.48`). The hook is `useTable` for every framework now; the
|
|
40
|
-
v8 name `useVueTable` no longer exists.
|
|
41
|
-
|
|
42
|
-
## Setup
|
|
43
|
-
|
|
44
|
-
Every Vue table call requires `features` (built from `tableFeatures({...})`). Row model
|
|
45
|
-
factories now live on the features object alongside the feature itself. Core row model is
|
|
46
|
-
automatic; only register `paginatedRowModel`, `sortedRowModel`, etc. when you use the matching
|
|
47
|
-
feature.
|
|
48
|
-
|
|
49
|
-
```vue
|
|
50
|
-
<script setup lang="ts">
|
|
51
|
-
import { ref } from 'vue'
|
|
52
|
-
import {
|
|
53
|
-
FlexRender,
|
|
54
|
-
createColumnHelper,
|
|
55
|
-
createSortedRowModel,
|
|
56
|
-
rowSortingFeature,
|
|
57
|
-
sortFns,
|
|
58
|
-
tableFeatures,
|
|
59
|
-
useTable,
|
|
60
|
-
} from '@tanstack/vue-table'
|
|
61
|
-
|
|
62
|
-
type Person = { firstName: string; lastName: string; age: number }
|
|
63
|
-
|
|
64
|
-
// Stable identity — declare outside the component or at module scope.
|
|
65
|
-
const features = tableFeatures({
|
|
66
|
-
rowSortingFeature,
|
|
67
|
-
sortedRowModel: createSortedRowModel(),
|
|
68
|
-
sortFns,
|
|
69
|
-
})
|
|
70
|
-
const columnHelper = createColumnHelper<typeof features, Person>()
|
|
71
|
-
const columns = columnHelper.columns([
|
|
72
|
-
columnHelper.accessor('firstName', { header: 'First' }),
|
|
73
|
-
columnHelper.accessor('lastName', { header: 'Last' }),
|
|
74
|
-
columnHelper.accessor('age', { header: 'Age' }),
|
|
75
|
-
])
|
|
76
|
-
|
|
77
|
-
const data = ref<Person[]>([])
|
|
78
|
-
|
|
79
|
-
const table = useTable({
|
|
80
|
-
features,
|
|
81
|
-
columns,
|
|
82
|
-
// Reactive data: pass the ref directly OR a getter — the adapter unwraps.
|
|
83
|
-
data,
|
|
84
|
-
})
|
|
85
|
-
</script>
|
|
86
|
-
|
|
87
|
-
<template>
|
|
88
|
-
<table>
|
|
89
|
-
<thead>
|
|
90
|
-
<tr v-for="hg in table.getHeaderGroups()" :key="hg.id">
|
|
91
|
-
<th v-for="h in hg.headers" :key="h.id">
|
|
92
|
-
<FlexRender v-if="!h.isPlaceholder" :header="h" />
|
|
93
|
-
</th>
|
|
94
|
-
</tr>
|
|
95
|
-
</thead>
|
|
96
|
-
<tbody>
|
|
97
|
-
<tr v-for="row in table.getRowModel().rows" :key="row.id">
|
|
98
|
-
<td v-for="cell in row.getAllCells()" :key="cell.id">
|
|
99
|
-
<FlexRender :cell="cell" />
|
|
100
|
-
</td>
|
|
101
|
-
</tr>
|
|
102
|
-
</tbody>
|
|
103
|
-
</table>
|
|
104
|
-
</template>
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Why this works
|
|
108
|
-
|
|
109
|
-
The Vue adapter calls `vueReactivity()` and installs it as `coreReactivityFeature` automatically
|
|
110
|
-
(see `packages/vue-table/src/useTable.ts`):
|
|
111
|
-
|
|
112
|
-
- Readonly atoms back onto `computed()` refs.
|
|
113
|
-
- Writable atoms back onto `shallowRef()`.
|
|
114
|
-
- Subscriptions use `watch(source, cb, { flush: 'sync' })`, so table updates are visible to Vue
|
|
115
|
-
render and computed work immediately.
|
|
116
|
-
|
|
117
|
-
`useTable` also runs a `watch(() => getReactiveOptionDeps(...))` on every option, so passing a
|
|
118
|
-
`ref` or `computed` for `data`, `columns`, `rowCount`, etc. is supported — the table calls
|
|
119
|
-
`setOptions` whenever any reactive option changes.
|
|
120
|
-
|
|
121
|
-
> **Vue note.** `table.Subscribe` exists for parity with React, but you usually do not need it.
|
|
122
|
-
> Vue's reactivity re-evaluates template reads automatically — wrap reads in `computed(...)` if
|
|
123
|
-
> you need them outside a template. Do not import React-Compiler workarounds.
|
|
124
|
-
|
|
125
|
-
## Core Patterns
|
|
126
|
-
|
|
127
|
-
### 1. The three read surfaces
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
// (a) Per-slice atom — narrowest, no full state snapshot built
|
|
131
|
-
const sorting = table.atoms.sorting.get()
|
|
132
|
-
|
|
133
|
-
// (b) Flat readonly store — every registered slice as one object
|
|
134
|
-
const snapshot = table.state
|
|
135
|
-
|
|
136
|
-
// (c) Vue selected state — the value returned from useTable's 2nd arg
|
|
137
|
-
const table = useTable(
|
|
138
|
-
{
|
|
139
|
-
features,
|
|
140
|
-
columns,
|
|
141
|
-
data,
|
|
142
|
-
},
|
|
143
|
-
(state) => ({ sorting: state.sorting }),
|
|
144
|
-
)
|
|
145
|
-
table.state.sorting // typed, reactive
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
`table.atoms.<slice>` only contains slices for features registered in `features`. If
|
|
149
|
-
`rowSortingFeature` is not registered, `table.atoms.sorting` is `undefined` (and TypeScript
|
|
150
|
-
flags it). This is the v9-specific "missing API" gotcha — register the feature first.
|
|
151
|
-
|
|
152
|
-
Source: `docs/framework/vue/guide/table-state.md` (Feature-based State, Accessing Table State).
|
|
153
|
-
|
|
154
|
-
### 2. Reactive data with a getter or computed
|
|
155
|
-
|
|
156
|
-
The adapter accepts a `ref`/`computed` for any option. The idiomatic shapes are:
|
|
157
|
-
|
|
158
|
-
```ts
|
|
159
|
-
// (a) Pass the ref directly — adapter unwraps via `unref()`
|
|
160
|
-
const data = ref(makeData(100))
|
|
161
|
-
const table = useTable({ features, columns, data })
|
|
162
|
-
|
|
163
|
-
// (b) Use a getter when `data` is owned by a parent object
|
|
164
|
-
const table = useTable({
|
|
165
|
-
features,
|
|
166
|
-
columns,
|
|
167
|
-
get data() {
|
|
168
|
-
return data.value
|
|
169
|
-
},
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
// (c) Computed when filtering/derivation lives on the client
|
|
173
|
-
const filtered = computed(() => data.value.filter(/* … */))
|
|
174
|
-
const table = useTable({
|
|
175
|
-
features,
|
|
176
|
-
columns,
|
|
177
|
-
get data() {
|
|
178
|
-
return filtered.value
|
|
179
|
-
},
|
|
180
|
-
})
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
When `data.value` changes, `useTable` calls `setOptions` synchronously and the table re-derives.
|
|
184
|
-
Source: `examples/vue/basic-use-table/src/App.tsx`, `examples/vue/virtualized-rows/src/App.vue`.
|
|
185
|
-
|
|
186
|
-
### 3. External atoms (recommended for shared state)
|
|
187
|
-
|
|
188
|
-
Use `createAtom` from `@tanstack/vue-store`; pass through `options.atoms`. Atoms take
|
|
189
|
-
precedence over `options.state` — pick one mechanism per slice.
|
|
190
|
-
|
|
191
|
-
```vue
|
|
192
|
-
<script setup lang="ts">
|
|
193
|
-
import { ref } from 'vue'
|
|
194
|
-
import { createAtom, useSelector } from '@tanstack/vue-store'
|
|
195
|
-
import {
|
|
196
|
-
createPaginatedRowModel,
|
|
197
|
-
rowPaginationFeature,
|
|
198
|
-
tableFeatures,
|
|
199
|
-
useTable,
|
|
200
|
-
type PaginationState,
|
|
201
|
-
} from '@tanstack/vue-table'
|
|
202
|
-
|
|
203
|
-
const features = tableFeatures({
|
|
204
|
-
rowPaginationFeature,
|
|
205
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
const paginationAtom = createAtom<PaginationState>({
|
|
209
|
-
pageIndex: 0,
|
|
210
|
-
pageSize: 10,
|
|
211
|
-
})
|
|
212
|
-
const pagination = useSelector(paginationAtom) // reactive ref-like
|
|
213
|
-
|
|
214
|
-
const data = ref([] as Person[])
|
|
215
|
-
const table = useTable({
|
|
216
|
-
features,
|
|
217
|
-
columns,
|
|
218
|
-
data,
|
|
219
|
-
atoms: { pagination: paginationAtom },
|
|
220
|
-
// NOTE: no `onPaginationChange` — `table.setPageIndex()` writes through to the atom.
|
|
221
|
-
})
|
|
222
|
-
</script>
|
|
223
|
-
|
|
224
|
-
<template>
|
|
225
|
-
<button @click="table.nextPage()" :disabled="!table.getCanNextPage()">
|
|
226
|
-
Next
|
|
227
|
-
</button>
|
|
228
|
-
<span>Page {{ pagination.value.pageIndex + 1 }}</span>
|
|
229
|
-
</template>
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Source: `examples/vue/basic-external-atoms/src/App.tsx`.
|
|
233
|
-
|
|
234
|
-
### 4. External `state` + `on[State]Change` with getters
|
|
235
|
-
|
|
236
|
-
Still supported and convenient for migration paths. The critical rule: pass each slice as a
|
|
237
|
-
**getter** so Vue can track `.value` changes. A raw ref captured in the state object is read
|
|
238
|
-
once and never re-tracked.
|
|
239
|
-
|
|
240
|
-
```ts
|
|
241
|
-
const sorting = ref<SortingState>([])
|
|
242
|
-
const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 10 })
|
|
243
|
-
|
|
244
|
-
const table = useTable({
|
|
245
|
-
features,
|
|
246
|
-
columns,
|
|
247
|
-
data,
|
|
248
|
-
state: {
|
|
249
|
-
get sorting() {
|
|
250
|
-
return sorting.value
|
|
251
|
-
}, // <- getter
|
|
252
|
-
get pagination() {
|
|
253
|
-
return pagination.value
|
|
254
|
-
}, // <- getter
|
|
255
|
-
},
|
|
256
|
-
onSortingChange: (u) => {
|
|
257
|
-
sorting.value = typeof u === 'function' ? u(sorting.value) : u
|
|
258
|
-
},
|
|
259
|
-
onPaginationChange: (u) => {
|
|
260
|
-
pagination.value = typeof u === 'function' ? u(pagination.value) : u
|
|
261
|
-
},
|
|
262
|
-
})
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
Source: `examples/vue/basic-external-state/src/App.tsx`,
|
|
266
|
-
`docs/framework/vue/guide/table-state.md` (External State).
|
|
267
|
-
|
|
268
|
-
### 5. `<FlexRender>` and `table.Subscribe`
|
|
269
|
-
|
|
270
|
-
`<FlexRender>` accepts a `cell`, `header`, or `footer` prop (preferred). The legacy
|
|
271
|
-
`:render` / `:props` pattern still works.
|
|
272
|
-
|
|
273
|
-
```vue
|
|
274
|
-
<FlexRender :cell="cell" />
|
|
275
|
-
<FlexRender :header="header" />
|
|
276
|
-
<FlexRender :footer="header" />
|
|
277
|
-
|
|
278
|
-
<!-- Legacy form -->
|
|
279
|
-
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
`table.Subscribe` mostly mirrors React. It is rarely the right tool in Vue — prefer
|
|
283
|
-
`computed(() => table.atoms.<slice>.get())` or the `useTable` selector. If you do need it
|
|
284
|
-
(for cross-store source subscription), it accepts a `source` prop:
|
|
285
|
-
|
|
286
|
-
```ts
|
|
287
|
-
// In a render function or JSX file
|
|
288
|
-
return () => (
|
|
289
|
-
<table.Subscribe source={someAtom} selector={(v) => v.someField}>
|
|
290
|
-
{(value) => <span>{value}</span>}
|
|
291
|
-
</table.Subscribe>
|
|
292
|
-
)
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
## Common Mistakes
|
|
296
|
-
|
|
297
|
-
### Passing a `ref` to `state.pagination` without a getter (CRITICAL)
|
|
298
|
-
|
|
299
|
-
```ts
|
|
300
|
-
// ❌ Captures the ref object; later `pagination.value = …` writes are invisible.
|
|
301
|
-
const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 10 })
|
|
302
|
-
const table = useTable({
|
|
303
|
-
features,
|
|
304
|
-
columns,
|
|
305
|
-
data,
|
|
306
|
-
state: { pagination },
|
|
307
|
-
})
|
|
308
|
-
|
|
309
|
-
// ✅ Use a getter so Vue tracks `.value`.
|
|
310
|
-
const table = useTable({
|
|
311
|
-
features,
|
|
312
|
-
columns,
|
|
313
|
-
data,
|
|
314
|
-
state: {
|
|
315
|
-
get pagination() {
|
|
316
|
-
return pagination.value
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
onPaginationChange: (u) => {
|
|
320
|
-
pagination.value = typeof u === 'function' ? u(pagination.value) : u
|
|
321
|
-
},
|
|
322
|
-
})
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Source: `docs/framework/vue/guide/table-state.md` (External State).
|
|
326
|
-
|
|
327
|
-
### Reading `table.atoms.<slice>.get()` outside a reactive context (HIGH)
|
|
328
|
-
|
|
329
|
-
```ts
|
|
330
|
-
// ❌ One-shot read at component setup — never updates.
|
|
331
|
-
const pagination = table.atoms.pagination.get()
|
|
332
|
-
|
|
333
|
-
// ✅ Wrap in `computed` so Vue tracks the atom.
|
|
334
|
-
const pagination = computed(() => table.atoms.pagination.get())
|
|
335
|
-
// or
|
|
336
|
-
const table = useTable(opts, (s) => ({ pagination: s.pagination }))
|
|
337
|
-
// then read `table.state.pagination`
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
### Using the v8 `useVueTable` name (HIGH)
|
|
341
|
-
|
|
342
|
-
```ts
|
|
343
|
-
// ❌ Removed in v9.
|
|
344
|
-
import { useVueTable } from '@tanstack/vue-table'
|
|
345
|
-
|
|
346
|
-
// ✅
|
|
347
|
-
import { useTable } from '@tanstack/vue-table'
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
### "API missing" because the feature is not in `features` (CRITICAL, v9-specific)
|
|
351
|
-
|
|
352
|
-
```ts
|
|
353
|
-
// ❌ `rowSortingFeature` not registered → `table.setSorting` and `table.atoms.sorting` do not exist.
|
|
354
|
-
const features = tableFeatures({})
|
|
355
|
-
const table = useTable({ features, columns, data })
|
|
356
|
-
table.setSorting([{ id: 'age', desc: true }]) // TS error / runtime no-op
|
|
357
|
-
|
|
358
|
-
// ✅ Register the feature and its matching row model factory inside tableFeatures.
|
|
359
|
-
const features = tableFeatures({
|
|
360
|
-
rowSortingFeature,
|
|
361
|
-
sortedRowModel: createSortedRowModel(),
|
|
362
|
-
sortFns,
|
|
363
|
-
})
|
|
364
|
-
const table = useTable({ features, columns, data })
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
### Reimplementing built-in state transitions (CRITICAL — #1 AI tell)
|
|
368
|
-
|
|
369
|
-
```ts
|
|
370
|
-
// ❌ Hand-rolled sort — bypasses the table's invariants and reset APIs.
|
|
371
|
-
const sorting = ref<SortingState>([])
|
|
372
|
-
const sorted = computed(() => [...data.value].sort(/* … */))
|
|
373
|
-
|
|
374
|
-
// ✅ Let the table own it. Use `table.setSorting`, `column.toggleSorting`,
|
|
375
|
-
// `header.column.getToggleSortingHandler()`.
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
The library exposes `setSorting`, `setColumnFilters`, `toggleSelected`, `nextPage`, etc. for
|
|
379
|
-
nearly every state transition.
|
|
380
|
-
|
|
381
|
-
### Hallucinating pre-v9 API names
|
|
382
|
-
|
|
383
|
-
`useVueTable`, `getCoreRowModel()` as an option, `createColumnHelper<TData>()` (single generic),
|
|
384
|
-
`sortingFn` instead of `sortFn` — all v8 shapes that will not compile. See
|
|
385
|
-
`migrate-v8-to-v9` for the full rename list.
|
|
386
|
-
|
|
387
|
-
### Unstable `features` / `columns` / `data` identity
|
|
388
|
-
|
|
389
|
-
Declare `features`, `columnHelper`, and `columns` **outside** `<script setup>` (at module
|
|
390
|
-
scope) or use `computed`. Recreating them every render churns the table's option diff watcher
|
|
391
|
-
and triggers a `setOptions` on every render — slow, and external atom slices can flicker.
|
|
392
|
-
|
|
393
|
-
## See Also
|
|
394
|
-
|
|
395
|
-
- `tanstack-table/vue/getting-started` — the end-to-end first-table walkthrough
|
|
396
|
-
- `tanstack-table/vue/production-readiness` — selectors, tree-shaking, identity
|
|
397
|
-
- `tanstack-table/vue/compose-with-tanstack-store` — external atoms in depth
|
|
398
|
-
- `tanstack-table/table-core/state-management` (core) — the atom model that drives this skill
|
|
399
|
-
- `tanstack-table/vue/migrate-v8-to-v9` — `useVueTable` → `useTable`
|