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