@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 CHANGED
@@ -40,6 +40,7 @@
40
40
  > - [Svelte Table](https://tanstack.com/table/alpha/docs/framework/svelte/svelte-table)
41
41
  > - [Vue Table](https://tanstack.com/table/alpha/docs/framework/vue/vue-table)
42
42
  > - [Alpine Table](https://tanstack.com/table/alpha/docs/framework/alpine/alpine-table)
43
+ > - [Ember Table](https://tanstack.com/table/alpha/docs/framework/ember/ember-table)
43
44
 
44
45
  A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
45
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/vue-table",
3
- "version": "9.0.0-beta.40",
3
+ "version": "9.0.0-beta.42",
4
4
  "description": "Headless UI for building powerful tables & datagrids for Vue.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -55,7 +55,7 @@
55
55
  ],
56
56
  "dependencies": {
57
57
  "@tanstack/store": "^0.11.0",
58
- "@tanstack/table-core": "9.0.0-beta.38"
58
+ "@tanstack/table-core": "9.0.0-beta.42"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@vitejs/plugin-vue": "^6.0.7",
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: create-table-hook
3
+ description: >
4
+ Create a reusable Vue useAppTable/createAppColumnHelper with shared features/defaults, reactive per-table options, optional component registries, dynamic App wrappers, typed context hooks, and explicit types that break circular inference.
5
+ metadata:
6
+ type: framework
7
+ library: '@tanstack/vue-table'
8
+ framework: vue
9
+ library_version: '9.0.0-beta.42'
10
+ requires:
11
+ - '@tanstack/table-core#core'
12
+ - getting-started
13
+ - table-state
14
+ sources:
15
+ - 'TanStack/table:docs/framework/vue/guide/composable-tables.md'
16
+ - 'TanStack/table:examples/vue/composable-tables'
17
+ - 'TanStack/table:packages/vue-table/src/createTableHook.ts'
18
+ ---
19
+
20
+ This skill builds on `@tanstack/table-core#core`, `getting-started`, and `table-state`. Use it for recurring app conventions; use `useTable` for a one-off.
21
+
22
+ ## Setup
23
+
24
+ ```ts
25
+ import {
26
+ createTableHook,
27
+ rowSortingFeature,
28
+ tableFeatures,
29
+ } from '@tanstack/vue-table'
30
+ import type { RowData, VueTable } from '@tanstack/vue-table'
31
+
32
+ const features = tableFeatures({ rowSortingFeature })
33
+ const hook = createTableHook({ features })
34
+ export const useAppTable = hook.useAppTable
35
+ export const createAppColumnHelper = hook.createAppColumnHelper
36
+ export const useTableContext: <TData extends RowData = RowData>() => VueTable<
37
+ typeof features,
38
+ TData
39
+ > = hook.useTableContext
40
+ ```
41
+
42
+ The explicit exported context-hook types are important when registered components import the hook module that also imports those components.
43
+
44
+ ## Core Patterns
45
+
46
+ ### Keep per-table values reactive
47
+
48
+ ```ts
49
+ const helper = createAppColumnHelper<Person>()
50
+ const columns = helper.columns([helper.accessor('name', { header: 'Name' })])
51
+ const table = useAppTable({ columns, data })
52
+ ```
53
+
54
+ Pass refs/computed options through unchanged.
55
+
56
+ ### Wrap registered component context
57
+
58
+ Render through `table.AppTable`, `table.AppCell`, or `table.AppHeader`; inside registered components call the corresponding typed context hook instead of prop drilling.
59
+
60
+ ## Common Mistakes
61
+
62
+ ### HIGH Creating circular inferred exports
63
+
64
+ Wrong:
65
+
66
+ ```ts
67
+ export const { useAppTable, useTableContext } = createTableHook({
68
+ tableComponents: { Pager },
69
+ })
70
+ ```
71
+
72
+ Correct:
73
+
74
+ ```ts
75
+ const hook = createTableHook({ tableComponents: { Pager } })
76
+ export const useTableContext: <TData extends RowData = RowData>() => VueTable<
77
+ typeof features,
78
+ TData
79
+ > = hook.useTableContext
80
+ ```
81
+
82
+ When `Pager` imports `useTableContext`, inferred destructured exports can form a circular inference/import chain.
83
+
84
+ Source: `docs/framework/vue/guide/composable-tables.md`
85
+
86
+ ### HIGH Flattening reactive table options
87
+
88
+ Wrong:
89
+
90
+ ```ts
91
+ useAppTable({ columns, data: data.value })
92
+ ```
93
+
94
+ Correct:
95
+
96
+ ```ts
97
+ useAppTable({ columns, data })
98
+ ```
99
+
100
+ The app hook preserves the adapter’s `MaybeRef` option contract.
101
+
102
+ Source: `packages/vue-table/src/createTableHook.ts`
103
+
104
+ ### HIGH Using context without App wrappers
105
+
106
+ Wrong:
107
+
108
+ ```ts
109
+ const table = useTableContext()
110
+ ```
111
+
112
+ Correct:
113
+
114
+ ```vue
115
+ <component :is="table.AppTable"><Pager /></component>
116
+ ```
117
+
118
+ The typed context exists only below the corresponding dynamic wrapper.
119
+
120
+ Source: `packages/vue-table/src/createTableHook.ts`
121
+
122
+ ### MEDIUM Treating Subscribe children as slots
123
+
124
+ Wrong:
125
+
126
+ ```tsx
127
+ <table.Subscribe>
128
+ {(atoms) => <Pager page={atoms.pagination.get()} />}
129
+ </table.Subscribe>
130
+ ```
131
+
132
+ Correct:
133
+
134
+ ```tsx
135
+ <table.Subscribe
136
+ children={(atoms) => <Pager page={atoms.pagination.get()} />}
137
+ />
138
+ ```
139
+
140
+ Vue’s adapter expects an explicit `children` prop in JSX.
141
+
142
+ Source: `packages/vue-table/src/useTable.ts`
143
+
144
+ ## API Discovery
145
+
146
+ Inspect `node_modules/@tanstack/vue-table/src/createTableHook.ts` for the returned helpers, wrapper props, registry types, and context contracts.
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: getting-started
3
+ description: >
4
+ Create a Vue TanStack Table v9 table with useTable, explicit tableFeatures, stable columns/features, reactive ref or computed data, and Vue FlexRender without destructuring reactive snapshots.
5
+ metadata:
6
+ type: framework
7
+ library: '@tanstack/vue-table'
8
+ framework: vue
9
+ library_version: '9.0.0-beta.42'
10
+ requires:
11
+ - '@tanstack/table-core#core'
12
+ - '@tanstack/table-core#table-features'
13
+ sources:
14
+ - 'TanStack/table:docs/framework/vue/guide/migrating.md'
15
+ - 'TanStack/table:examples/vue/basic-use-table'
16
+ - 'TanStack/table:packages/vue-table/src/index.ts'
17
+ ---
18
+
19
+ This skill builds on `@tanstack/table-core#core` and `@tanstack/table-core#table-features`. Read them first for the headless model and explicit feature registration.
20
+
21
+ ## Setup
22
+
23
+ ```vue
24
+ <script setup lang="ts">
25
+ import { ref } from 'vue'
26
+ import { FlexRender, tableFeatures, useTable } from '@tanstack/vue-table'
27
+
28
+ type Person = { name: string; age: number }
29
+ const features = tableFeatures({})
30
+ const columns = [
31
+ { accessorKey: 'name', header: 'Name' },
32
+ { accessorKey: 'age', header: 'Age' },
33
+ ]
34
+ const data = ref<Person[]>([{ name: 'Ada', age: 36 }])
35
+ const table = useTable({ features, columns, data })
36
+ </script>
37
+
38
+ <template>
39
+ <table>
40
+ <thead>
41
+ <tr v-for="group in table.getHeaderGroups()" :key="group.id">
42
+ <th v-for="header in group.headers" :key="header.id">
43
+ <FlexRender v-if="!header.isPlaceholder" :header="header" />
44
+ </th>
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ <tr v-for="row in table.getRowModel().rows" :key="row.id">
49
+ <td v-for="cell in row.getAllCells()" :key="cell.id">
50
+ <FlexRender :cell="cell" />
51
+ </td>
52
+ </tr>
53
+ </tbody>
54
+ </table>
55
+ </template>
56
+ ```
57
+
58
+ ## Core Patterns
59
+
60
+ ### Preserve Vue option shapes
61
+
62
+ `useTable` accepts refs/computed values and unwraps them while watching dependencies. Keep `data`, controlled state, and other reactive options as refs or computed values; keep static columns/features stable.
63
+
64
+ ### Add a client row model explicitly
65
+
66
+ ```ts
67
+ import {
68
+ createSortedRowModel,
69
+ rowSortingFeature,
70
+ sortFns,
71
+ tableFeatures,
72
+ } from '@tanstack/vue-table'
73
+
74
+ const features = tableFeatures({
75
+ rowSortingFeature,
76
+ sortedRowModel: createSortedRowModel(),
77
+ sortFns,
78
+ })
79
+ ```
80
+
81
+ The slot follows its prerequisite feature in the same call.
82
+
83
+ ## Common Mistakes
84
+
85
+ ### HIGH Flattening a ref into a snapshot
86
+
87
+ Wrong:
88
+
89
+ ```ts
90
+ const table = useTable({ features, columns, data: data.value })
91
+ ```
92
+
93
+ Correct:
94
+
95
+ ```ts
96
+ const table = useTable({ features, columns, data })
97
+ ```
98
+
99
+ Passing `.value` captures one array instead of letting the adapter watch the ref.
100
+
101
+ Source: `packages/vue-table/src/useTable.ts`
102
+
103
+ ### HIGH Using the v8 entrypoint
104
+
105
+ Wrong:
106
+
107
+ ```ts
108
+ const table = useVueTable({ data, columns, getCoreRowModel: getCoreRowModel() })
109
+ ```
110
+
111
+ Correct:
112
+
113
+ ```ts
114
+ const table = useTable({ features, columns, data })
115
+ ```
116
+
117
+ V9 uses `useTable`; core processing is automatic and optional row models live in `tableFeatures`.
118
+
119
+ Source: `docs/framework/vue/guide/migrating.md`
120
+
121
+ ### HIGH Assuming headless means prebuilt UI
122
+
123
+ Wrong:
124
+
125
+ ```vue
126
+ <TanStackTable :table="table" />
127
+ ```
128
+
129
+ Correct:
130
+
131
+ ```vue
132
+ <td
133
+ v-for="cell in row.getAllCells()"
134
+ :key="cell.id"
135
+ ><FlexRender :cell="cell" /></td>
136
+ ```
137
+
138
+ The adapter renders definitions but owns no table component, CSS, or design-system integration.
139
+
140
+ Source: `examples/vue/basic-use-table/src/App.tsx`
141
+
142
+ ## API Discovery
143
+
144
+ Inspect `node_modules/@tanstack/vue-table/src/index.ts`, then `useTable.ts` and `FlexRender.ts`. Inspect core feature APIs in `node_modules/@tanstack/table-core/src/features/<feature>/`.
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: migrate-v8-to-v9
3
+ description: >
4
+ Complete Vue v8-to-v9 migration reference: useTable, explicit features and row-model slots, ref/atom state, FlexRender shorthand, prototype methods, type generics, sorting, sizing, selection, and logical pinning.
5
+ metadata:
6
+ type: lifecycle
7
+ library: '@tanstack/vue-table'
8
+ framework: vue
9
+ library_version: '9.0.0-beta.42'
10
+ requires:
11
+ - '@tanstack/table-core#migrate-v8-to-v9'
12
+ - getting-started
13
+ - table-state
14
+ sources:
15
+ - 'TanStack/table:docs/framework/vue/guide/migrating.md'
16
+ - 'TanStack/table:packages/vue-table/src/index.ts'
17
+ - 'TanStack/table:examples/vue/basic-use-table'
18
+ ---
19
+
20
+ Use this as the complete breaking-change checklist. V9 is the current API; do not stop after renaming the Vue composable.
21
+
22
+ Framework prerequisite: Vue 3.2 or newer (`vue >=3.2`).
23
+
24
+ ## Recommended Migration Order
25
+
26
+ 1. Replace `useVueTable` with `useTable` while preserving reactive inputs.
27
+ 2. Define explicit features, then move row models and registries into `tableFeatures`.
28
+ 3. Update state reads, controlled ownership, and rendering.
29
+ 4. Apply every shared API and type rename below.
30
+ 5. Use `stockFeatures` only as a temporary audit bridge; explicit features are the production target.
31
+
32
+ ```ts
33
+ const features = tableFeatures({
34
+ rowSortingFeature,
35
+ sortedRowModel: createSortedRowModel(),
36
+ sortFns,
37
+ })
38
+ const data = ref(makeData())
39
+ const table = useTable({ features, columns, data })
40
+ ```
41
+
42
+ ## Construction and Feature Registration
43
+
44
+ | v8 | v9 |
45
+ | -------------------------------------------- | ---------------------------------------------------------- |
46
+ | `useVueTable(options)` | `useTable(options)` |
47
+ | All features bundled | Required `features: tableFeatures({...})` |
48
+ | `getCoreRowModel()` option | Remove; core row model is automatic |
49
+ | `get*RowModel()` table options | `create*RowModel()` slots in `tableFeatures` |
50
+ | `sortingFns` table option | `sortFns` feature slot |
51
+ | `filterFns` / `aggregationFns` table options | Same-named feature slots |
52
+ | Top-level `onStateChange` | Per-slice callbacks, external atoms, or store subscription |
53
+
54
+ Available feature imports are `columnFilteringFeature`, `globalFilteringFeature`, `rowSortingFeature`, `rowPaginationFeature`, `rowSelectionFeature`, `rowExpandingFeature`, `rowPinningFeature`, `columnPinningFeature`, `columnVisibilityFeature`, `columnOrderingFeature`, `columnSizingFeature`, `columnResizingFeature`, `columnGroupingFeature`, and `columnFacetingFeature`. APIs are feature-gated. Put every feature before its dependent slot in the same `tableFeatures` call.
55
+
56
+ ### Row-model mapping
57
+
58
+ | v8 option | v9 slot and factory |
59
+ | -------------------------- | ------------------------------------------------------------------- |
60
+ | `getFilteredRowModel()` | `filteredRowModel: createFilteredRowModel()` after column filtering |
61
+ | `getSortedRowModel()` | `sortedRowModel: createSortedRowModel()` after row sorting |
62
+ | `getPaginationRowModel()` | `paginatedRowModel: createPaginatedRowModel()` after pagination |
63
+ | `getExpandedRowModel()` | `expandedRowModel: createExpandedRowModel()` after expanding |
64
+ | `getGroupedRowModel()` | `groupedRowModel: createGroupedRowModel()` after grouping |
65
+ | `getFacetedRowModel()` | `facetedRowModel: createFacetedRowModel()` after faceting |
66
+ | `getFacetedMinMaxValues()` | `facetedMinMaxValues: createFacetedMinMaxValues()` |
67
+ | `getFacetedUniqueValues()` | `facetedUniqueValues: createFacetedUniqueValues()` |
68
+
69
+ Factories take no arguments. Register `filterFns`, `sortFns`, and `aggregationFns` as sibling feature slots.
70
+
71
+ ## Vue State Migration
72
+
73
+ - Pass a `ref` or `computed` as `data`; the adapter unwraps and syncs it. Do not pass `data.value`, which is only a snapshot. A getter returning `data.value` is also supported.
74
+ - `table.getState().sorting` becomes the narrow `table.atoms.sorting.get()`. Use `table.store.get()` only for a full snapshot/debug output.
75
+ - Wrap atom reads in Vue `computed` when deriving template values.
76
+ - In JSX/render functions, `table.Subscribe` provides a fine-grained boundary. Pass the callback as the explicit `children` prop because Vue JSX element children become slots.
77
+ - Controlled refs need getter-backed state slices plus per-slice callbacks that resolve value-or-function `Updater`s.
78
+ - The top-level `onStateChange` is removed. Use per-slice callbacks, external atoms, or `table.store.subscribe` to observe everything.
79
+ - External atoms come from `@tanstack/vue-store` and are supplied through `atoms`. Never provide both `atoms.pagination` and `state.pagination`.
80
+ - `table.baseAtoms` is internal writable state; prefer feature APIs or external atoms.
81
+
82
+ ## Rendering and Composition
83
+
84
+ | v8 | v9 target |
85
+ | -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
86
+ | `<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />` | `<FlexRender :cell="cell" />` |
87
+ | Manual header/footer render props | `<FlexRender :header="header" />` / `:footer="footer"` |
88
+ | Repeated raw options | `tableOptions(...)` composition |
89
+ | Repeated table conventions | `createTableHook({ features, ... })` and pre-bound helpers |
90
+
91
+ The old `render`/`props` FlexRender shape still compiles, but shorthand is the migration target. `createTableHook` is optional and intended for application-wide conventions.
92
+
93
+ ## Complete Shared Breaking-Change Map
94
+
95
+ ### Instance methods
96
+
97
+ Row, cell, column, header, and related methods now live on shared prototypes and use `this`. Call them on their instances. Do not destructure them, pass them bare, or expect them in object spread, `Object.keys`, or JSON. Table methods are not affected.
98
+
99
+ ### Logical column pinning
100
+
101
+ There are no `left`/`right` aliases in beta.38.
102
+
103
+ | old | new |
104
+ | -------------------------------------------------------------- | ------------------------------------------------------------- |
105
+ | `columnPinning.left` / `.right` | `.start` / `.end` |
106
+ | `column.pin('left' \| 'right')` | `column.pin('start' \| 'end')` |
107
+ | `getIsPinned() === 'left' \| 'right'` | `'start' \| 'end'` |
108
+ | `row.getLeftVisibleCells()` / `getRightVisibleCells()` | `getStartVisibleCells()` / `getEndVisibleCells()` |
109
+ | `getLeftHeaderGroups()` / `getRightHeaderGroups()` | `getStartHeaderGroups()` / `getEndHeaderGroups()` |
110
+ | `getLeftFooterGroups()` / `getRightFooterGroups()` | `getStartFooterGroups()` / `getEndFooterGroups()` |
111
+ | `getLeftFlatHeaders()` / `getRightFlatHeaders()` | `getStartFlatHeaders()` / `getEndFlatHeaders()` |
112
+ | `getLeftLeafHeaders()` / `getRightLeafHeaders()` | `getStartLeafHeaders()` / `getEndLeafHeaders()` |
113
+ | `getLeftLeafColumns()` / `getRightLeafColumns()` | `getStartLeafColumns()` / `getEndLeafColumns()` |
114
+ | `getLeftVisibleLeafColumns()` / `getRightVisibleLeafColumns()` | `getStartVisibleLeafColumns()` / `getEndVisibleLeafColumns()` |
115
+ | `getLeftTotalSize()` / `getRightTotalSize()` | `getStartTotalSize()` / `getEndTotalSize()` |
116
+ | `column.getStart('left')` | `column.getStart('start')` |
117
+ | `column.getAfter('right')` | `column.getAfter('end')` |
118
+ | `column.getIndex('left' \| 'right')` | `column.getIndex('start' \| 'end')` |
119
+
120
+ Use CSS `inset-inline-start`/`inset-inline-end`; logical names do not automatically set DOM direction. `columnResizeDirection` is unchanged.
121
+
122
+ ### Pinning, sizing, and resizing
123
+
124
+ - `enablePinning` splits into `enableColumnPinning` and `enableRowPinning`.
125
+ - Interactive resizing requires `columnSizingFeature` plus `columnResizingFeature`; fixed sizing needs only the former.
126
+ - `columnSizingInfo` becomes `columnResizing`.
127
+ - `setColumnSizingInfo()` becomes `setColumnResizing()`.
128
+ - `onColumnSizingInfoChange` becomes `onColumnResizingChange`.
129
+
130
+ ### Sorting, rows, and selection
131
+
132
+ | v8 | v9 |
133
+ | ------------------------------ | ----------------------------- |
134
+ | `sortingFn` | `sortFn` |
135
+ | `sortingFns` | `sortFns` |
136
+ | `getSortingFn()` | `getSortFn()` |
137
+ | `getAutoSortingFn()` | `getAutoSortFn()` |
138
+ | `SortingFn` / `SortingFns` | `SortFn` / `SortFns` |
139
+ | `row._getAllCellsByColumnId()` | `row.getAllCellsByColumnId()` |
140
+
141
+ Other `_`-prefixed internals are removed, including `_getPinnedRows`, `_getFacetedRowModel`, `_getFacetedMinMaxValues`, and `_getFacetedUniqueValues`.
142
+
143
+ `getIsSomeRowsSelected()` and `getIsSomePageRowsSelected()` now mean at least one, including all. Indeterminate UI must also check `!getIsAllRowsSelected()` or `!getIsAllPageRowsSelected()`.
144
+
145
+ ## TypeScript Migration
146
+
147
+ - Add `TFeatures` first: `ColumnDef<typeof features, Person>`, `Column<typeof features, Person>`, `Row<typeof features, Person>`, `Table<typeof features, Person>`.
148
+ - Replace `createColumnHelper<Person>()` with `createColumnHelper<typeof features, Person>()`; use `columnHelper.columns([...])` for nested-array inference.
149
+ - Use `StockFeatures` when `stockFeatures` is the configuration.
150
+ - Existing `TableMeta`/`ColumnMeta` declaration merging must add `TFeatures` first. Prefer per-table `tableMeta`/`columnMeta: metaHelper<...>()` slots.
151
+ - Replace global `FilterFns`, `SortFns`, `AggregationFns`, and `FilterMeta` augmentation with registry slots and `filterMeta: metaHelper<...>()`; registered keys become valid strings in column defs.
152
+ - `RowData` is restricted to records or arrays; prefer explicit object row types.
153
+
154
+ ## Common Migration Failures
155
+
156
+ ### HIGH: Renaming only the composable
157
+
158
+ `useTable({ getSortedRowModel: ... })` is still a v8 configuration. Move the row model and its prerequisite feature into `tableFeatures`.
159
+
160
+ ### HIGH: Unwrapping refs before useTable
161
+
162
+ Pass `data`, not `data.value`, or use a getter. Preserve the reactive source.
163
+
164
+ ### HIGH: Passing prototype methods bare
165
+
166
+ Use `row.getValue('name')`, not `const read = row.getValue`; shallow copies also lose methods.
167
+
168
+ ### MEDIUM: JSX children as slots
169
+
170
+ For `table.Subscribe`, use `children={(atoms) => ...}` explicitly.
171
+
172
+ ## Final Checklist
173
+
174
+ - [ ] `useVueTable` is replaced with `useTable`; refs/computed inputs remain reactive.
175
+ - [ ] Features, row models, and registries are in `tableFeatures`; core row model is removed.
176
+ - [ ] State reads use atoms/computed or the store intentionally; `onStateChange` is removed.
177
+ - [ ] External atom and controlled state ownership do not overlap.
178
+ - [ ] FlexRender shorthand is adopted where applicable.
179
+ - [ ] Prototype methods, pinning, sizing/resizing, sorting, row, and selection changes are audited.
180
+ - [ ] Helpers, types, meta, registries, and `RowData` use v9 shapes.
181
+ - [ ] Temporary `stockFeatures` usage has an explicit removal plan.
182
+
183
+ ## API Discovery
184
+
185
+ Inspect `node_modules/@tanstack/vue-table/src/index.ts` and `useTable.ts`; verify feature slots and exact beta APIs in `node_modules/@tanstack/table-core/src`. Do not reconstruct v9 from v8 memory.
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: table-state
3
+ description: >
4
+ Read Vue-backed table.atoms/store in templates, computed, watch, or table.Subscribe; own slices with refs/computed or external Vue Store atoms; and apply updater callbacks while preserving reactive option shapes.
5
+ metadata:
6
+ type: framework
7
+ library: '@tanstack/vue-table'
8
+ framework: vue
9
+ library_version: '9.0.0-beta.42'
10
+ requires:
11
+ - '@tanstack/table-core#core'
12
+ - getting-started
13
+ sources:
14
+ - 'TanStack/table:docs/framework/vue/guide/table-state.md'
15
+ - 'TanStack/table:examples/vue/basic-external-state'
16
+ - 'TanStack/table:packages/vue-table/src/useTable.ts'
17
+ ---
18
+
19
+ This skill builds on `@tanstack/table-core#core` and `getting-started`. Read them first for state ownership and Vue construction.
20
+
21
+ ## State Mental Model
22
+
23
+ TanStack Table is primarily a state coordinator. Keep state internal unless another system needs to read, persist, or drive it. Without `initialState`, `atoms`, `state`, or `on[State]Change`, the table owns every registered slice.
24
+
25
+ - `table.baseAtoms` are internal writable atoms created from resolved initial state.
26
+ - `table.atoms` are readonly derived atoms for the active owner of each registered slice.
27
+ - `table.store` combines those atoms into one readonly flat store.
28
+
29
+ The Vue adapter backs atoms with refs/computed values and tracks reactive table options. Atom reads become reactive inside templates, `computed`, `watch`, or `table.Subscribe`; a read cached outside tracking is only a snapshot. State is feature-based, so a missing pagination atom or option means `rowPaginationFeature` was not registered. Keep `features` and `columns` stable; pass reactive `data` as a ref/computed instead of recreating arrays in table options.
30
+
31
+ ## Setup
32
+
33
+ ```ts
34
+ import { computed, ref } from 'vue'
35
+ import {
36
+ rowPaginationFeature,
37
+ tableFeatures,
38
+ useTable,
39
+ } from '@tanstack/vue-table'
40
+
41
+ const features = tableFeatures({ rowPaginationFeature })
42
+ const data = ref([{ name: 'Ada' }])
43
+ const columns = [{ accessorKey: 'name' }]
44
+ const table = useTable({ features, columns, data })
45
+ const pageIndex = computed(() => table.atoms.pagination.get().pageIndex)
46
+ ```
47
+
48
+ Internal state is usually enough. Atom reads are reactive only when Vue evaluates them in a tracked template, computed, watch, or render boundary.
49
+
50
+ ## Core Patterns
51
+
52
+ ### Control a slice without losing updater semantics
53
+
54
+ ```ts
55
+ import { computed, ref } from 'vue'
56
+ import type { PaginationState } from '@tanstack/vue-table'
57
+
58
+ const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 20 })
59
+ const controlledState = computed(() => ({ pagination: pagination.value }))
60
+ const onPaginationChange = (
61
+ next: PaginationState | ((old: PaginationState) => PaginationState),
62
+ ) => {
63
+ pagination.value = typeof next === 'function' ? next(pagination.value) : next
64
+ }
65
+ ```
66
+
67
+ Pass `state: controlledState` and `onPaginationChange` to `useTable`.
68
+
69
+ ### Use Subscribe as a render boundary
70
+
71
+ ```tsx
72
+ table.Subscribe({
73
+ children: (atoms) => <span>{atoms.pagination.get().pageIndex + 1}</span>,
74
+ })
75
+ ```
76
+
77
+ In Vue JSX, `children` is an explicit prop, not a slot child.
78
+
79
+ ## Choose State Ownership
80
+
81
+ Use exactly one owner per slice:
82
+
83
+ - Prefer internal state and feature methods for table-local behavior.
84
+ - Use `initialState` for starting/reset values; changing it later does not reset current state.
85
+ - Prefer a stable `@tanstack/vue-store` atom in `atoms` for cross-system ownership. Feature APIs update it directly, so omit `on[State]Change`.
86
+ - Use a ref/computed `state` value plus the matching callback for simple controlled state. Preserve the reactive wrapper and resolve raw values and updater functions.
87
+
88
+ External atoms take precedence over external `state`, which syncs into the internal base atom. Do not configure multiple owners. The global v8 `onStateChange` option is gone; observe `table.store` if all state changes matter.
89
+
90
+ ## Initialize, Update, and Reset
91
+
92
+ Use feature methods such as `setSorting`, `nextPage`, `toggleVisibility`, and `toggleSelected`. Direct `baseAtoms` writes are a rare escape hatch for internally owned state; write the external atom when it owns the slice.
93
+
94
+ ```ts
95
+ table.resetSorting()
96
+ table.resetPagination()
97
+ table.resetPagination(true)
98
+ ```
99
+
100
+ Feature resets use `table.initialState` unless `true` requests the feature default and can update external owners. Core `table.reset()` only resets internal base atoms. Use slice types such as `PaginationState`; use `TableState<typeof features>` for the complete feature-inferred state.
101
+
102
+ ## Common Mistakes
103
+
104
+ ### HIGH Reading an untracked snapshot
105
+
106
+ Wrong:
107
+
108
+ ```ts
109
+ const pageIndex = table.atoms.pagination.get().pageIndex
110
+ ```
111
+
112
+ Correct:
113
+
114
+ ```ts
115
+ const pageIndex = computed(() => table.atoms.pagination.get().pageIndex)
116
+ ```
117
+
118
+ The first read is current but does not make its consumer reactive.
119
+
120
+ Source: `docs/framework/vue/guide/table-state.md`
121
+
122
+ ### HIGH Passing state.value once
123
+
124
+ Wrong:
125
+
126
+ ```ts
127
+ const table = useTable({
128
+ features,
129
+ columns,
130
+ data,
131
+ state: controlledState.value,
132
+ })
133
+ ```
134
+
135
+ Correct:
136
+
137
+ ```ts
138
+ const table = useTable({ features, columns, data, state: controlledState })
139
+ ```
140
+
141
+ The adapter watches the computed ref; a one-time `.value` breaks future option synchronization.
142
+
143
+ Source: `packages/vue-table/src/useTable.ts`
144
+
145
+ ### HIGH Assigning updater functions as values
146
+
147
+ Wrong:
148
+
149
+ ```ts
150
+ const onPaginationChange = (next) => {
151
+ pagination.value = next
152
+ }
153
+ ```
154
+
155
+ Correct:
156
+
157
+ ```ts
158
+ const onPaginationChange = (next) => {
159
+ pagination.value = typeof next === 'function' ? next(pagination.value) : next
160
+ }
161
+ ```
162
+
163
+ Table callbacks accept either a value or a function of the previous value.
164
+
165
+ Source: `examples/vue/basic-external-state/src/App.tsx`
166
+
167
+ ### MEDIUM Supplying JSX children as a slot
168
+
169
+ Wrong:
170
+
171
+ ```tsx
172
+ <table.Subscribe>
173
+ {(atoms) => <span>{atoms.pagination.get().pageIndex}</span>}
174
+ </table.Subscribe>
175
+ ```
176
+
177
+ Correct:
178
+
179
+ ```tsx
180
+ <table.Subscribe
181
+ children={(atoms) => <span>{atoms.pagination.get().pageIndex}</span>}
182
+ />
183
+ ```
184
+
185
+ The Vue adapter declares `Subscribe(props: { children })` and expects the explicit prop.
186
+
187
+ Source: `packages/vue-table/src/useTable.ts`
188
+
189
+ ## API Discovery
190
+
191
+ Inspect `node_modules/@tanstack/vue-table/src/useTable.ts` and `reactivity.ts`; inspect the exact state slice in the installed core feature directory.