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.
60
72%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./packages/vue-table/skills/table-state/SKILL.mdThis skill builds on @tanstack/table-core#core and getting-started. Read them first for state ownership and Vue construction.
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.
table.baseAtoms are internal writable atoms created from resolved initial state.table.atoms are readonly derived atoms for the active owner of each registered slice.table.store combines those atoms into one readonly flat store.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.
import { computed, ref } from 'vue'
import {
rowPaginationFeature,
tableFeatures,
useTable,
} from '@tanstack/vue-table'
const features = tableFeatures({ rowPaginationFeature })
const data = ref([{ name: 'Ada' }])
const columns = [{ accessorKey: 'name' }]
const table = useTable({ features, columns, data })
const pageIndex = computed(() => table.atoms.pagination.get().pageIndex)Internal state is usually enough. Atom reads are reactive only when Vue evaluates them in a tracked template, computed, watch, or render boundary.
import { computed, ref } from 'vue'
import type { PaginationState } from '@tanstack/vue-table'
const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 20 })
const controlledState = computed(() => ({ pagination: pagination.value }))
const onPaginationChange = (
next: PaginationState | ((old: PaginationState) => PaginationState),
) => {
pagination.value = typeof next === 'function' ? next(pagination.value) : next
}Pass state: controlledState and onPaginationChange to useTable.
table.Subscribe({
children: (atoms) => <span>{atoms.pagination.get().pageIndex + 1}</span>,
})In Vue JSX, children is an explicit prop, not a slot child.
Use exactly one owner per slice:
initialState for starting/reset values; changing it later does not reset current state.@tanstack/vue-store atom in atoms for cross-system ownership. Feature APIs update it directly, so omit on[State]Change.state value plus the matching callback for simple controlled state. Preserve the reactive wrapper and resolve raw values and updater functions.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.
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.
table.resetSorting()
table.resetPagination()
table.resetPagination(true)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.
Wrong:
const pageIndex = table.atoms.pagination.get().pageIndexCorrect:
const pageIndex = computed(() => table.atoms.pagination.get().pageIndex)The first read is current but does not make its consumer reactive.
Source: docs/framework/vue/guide/table-state.md
Wrong:
const table = useTable({
features,
columns,
data,
state: controlledState.value,
})Correct:
const table = useTable({ features, columns, data, state: controlledState })The adapter watches the computed ref; a one-time .value breaks future option synchronization.
Source: packages/vue-table/src/useTable.ts
Wrong:
const onPaginationChange = (next) => {
pagination.value = next
}Correct:
const onPaginationChange = (next) => {
pagination.value = typeof next === 'function' ? next(pagination.value) : next
}Table callbacks accept either a value or a function of the previous value.
Source: examples/vue/basic-external-state/src/App.tsx
Wrong:
<table.Subscribe>
{(atoms) => <span>{atoms.pagination.get().pageIndex}</span>}
</table.Subscribe>Correct:
<table.Subscribe
children={(atoms) => <span>{atoms.pagination.get().pageIndex}</span>}
/>The Vue adapter declares Subscribe(props: { children }) and expects the explicit prop.
Source: packages/vue-table/src/useTable.ts
Inspect node_modules/@tanstack/vue-table/dist/useTable.d.ts and reactivity.d.ts; inspect the exact state slice in the installed core feature directory.
9e523bc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.