---
name: tanstack-vue-table-skilld
description: Use when writing, migrating, or debugging code that imports @tanstack/vue-table (TanStack Table v9 for Vue). Covers useTable with explicit tableFeatures, reactive ref/computed data, FlexRender rendering, atoms-based state, createTableHook conventions, and the v8-to-v9 breaking changes.
---
# @tanstack/vue-table 9.2.4
Headless table adapter for Vue 3. Re-exports all of `@tanstack/table-core` 9.2.4 plus Vue bindings: `useTable`, `FlexRender`, `flexRender`, `createTableHook` (`dist/index.d.ts:4-5`).
Environment limits: `vue >=3.2` peer dependency, Node >=20, runtime dep `@tanstack/store ^0.11.1` (`package.json:34-53`).
This Skill targets v9 only. Code using `useVueTable`, `getCoreRowModel()`, or `onStateChange` is v8; migrate it first with [Migration v8 to v9](./references/migration-v8-to-v9.md).
## Quick start
```vue
```
Source: `skills/getting-started/SKILL.md` (shipped in the package).
## Add features explicitly
Every feature beyond core must be registered, and each row-model slot follows its prerequisite feature in the same `tableFeatures` call. The core row model is automatic; there is no `getCoreRowModel()` option.
```ts
import {
createSortedRowModel,
rowSortingFeature,
sortFn_alphanumeric,
tableFeatures,
} from '@tanstack/vue-table'
const features = tableFeatures({
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
sortFns: { alphanumeric: sortFn_alphanumeric },
})
```
APIs are feature-gated: a missing `table.atoms.pagination` means `rowPaginationFeature` was not registered. Full feature and slot list: [API surface](./references/api.md).
## State in one place per slice
State is atom-based. Read it inside a tracked boundary (template, `computed`, `watch`, or `table.Subscribe`); a bare read is only a snapshot.
```ts
const pageIndex = computed(() => table.atoms.pagination.get().pageIndex)
```
Controlled slices need a reactive `state` plus the matching `on[State]Change` callback that resolves value-or-function updaters:
```ts
const pagination = ref({ pageIndex: 0, pageSize: 20 })
const state = computed(() => ({ pagination: pagination.value }))
const onPaginationChange = (next: PaginationState | ((old: PaginationState) => PaginationState)) => {
pagination.value = typeof next === 'function' ? next(pagination.value) : next
}
// pass state and onPaginationChange to useTable
```
Own each slice exactly once: internal default, `initialState`, external `atoms`, or `state` + callback. Never both `atoms.pagination` and `state.pagination`. `onStateChange` no longer exists; observe `table.store` instead. Details: [API surface](./references/api.md).
## Rules that prevent most bugs
1. Pass `data` as a `ref`/`computed`, never `data.value` (`dist/useTable.d.ts:13-19`). Update it by replacing the array (`.value = [...]`); in-place `push`/`splice` is not tracked because writable atoms use `shallowRef` (`dist/reactivity.js:50-52`).
2. Keep `features` and `columns` stable; do not rebuild arrays per render.
3. Render through `FlexRender` shorthand: `:cell`, `:header`, `:footer`. The legacy `:render`/`:props` form still compiles but is the migration target, not the default (`dist/FlexRender.d.ts:28-41`).
4. Never destructure or spread row/cell/column/header methods; they are prototype methods using `this`. Use `row.getValue('name')`, not `const read = row.getValue`.
5. In JSX, pass `table.Subscribe` its render function as the explicit `children` prop; Vue JSX children become slots (`dist/useTable.d.ts:9-11`).
6. For server-driven tables set the `manual*` option (`manualPagination: true`) and pass `rowCount`.
7. Optional `stockFeatures` bundles everything; use it only as a temporary audit bridge, not production.
## Common mistakes
- HIGH: renaming `useVueTable` to `useTable` while keeping `getSortedRowModel` options. Move row models and registries into `tableFeatures` (`skills/migrate-v8-to-v9/SKILL.md:156-159`).
- HIGH: `useTable({ data: data.value })` freezes one snapshot; pass `data` (`skills/getting-started/SKILL.md:85-99`).
- HIGH: reading `table.atoms.sorting.get()` outside a tracked boundary; wrap in `computed` (`skills/table-state/SKILL.md:104-116`).
- HIGH: assigning the updater function itself (`pagination.value = next`) instead of resolving it first (`skills/table-state/SKILL.md:146-161`).
- HIGH: `columnPinning.left`/`right` no longer exist; v9 uses `start`/`end` logical pinning (`skills/migrate-v8-to-v9/SKILL.md:99-120`).
- HIGH: `createColumnHelper()` now needs features first: `createColumnHelper()` (`skills/migrate-v8-to-v9/SKILL.md:147-148`).
## Reusable app conventions
For app-wide table defaults, use `createTableHook({ features, tableComponents, cellComponents, headerComponents })` to get `useAppTable`, `createAppColumnHelper`, and typed context hooks (`dist/createTableHook.d.ts:136-153`). Export the context hook with an explicit type to avoid circular inference when registered components import the hook module (`skills/create-table-hook/SKILL.md:62-84`).
## Integrations
- TanStack Query: manual pagination with reactive keys, `rowCount`, computed data passthrough. [Integrations](./references/integrations.md)
- TanStack Virtual: virtualize `table.getRowModel().rows`, never the source array. [Integrations](./references/integrations.md)
## References
- [API surface](./references/api.md): exports, subpaths, `useTable`/`Subscribe` types, features and slots, atoms model.
- [Migration v8 to v9](./references/migration-v8-to-v9.md): full breaking-change map, pinning renames, TypeScript changes, checklist.
- [Integrations](./references/integrations.md): TanStack Query and TanStack Virtual composition patterns.
## Also in the package
The package ships its own agent skills under `skills/` (getting-started, table-state, migrate-v8-to-v9, create-table-hook, with-tanstack-query, with-tanstack-virtual) and mentions `npx @tanstack/intent@latest install` to wire them into coding agents (`README.md:70-78`). Verify exact APIs in the installed `node_modules/@tanstack/vue-table/dist/index.d.ts`; do not reconstruct v9 from v8 memory.