Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: tanstack-vue-query-skilld description: "Hooks for managing, caching and syncing asynchronous and remote data in Vue. ALWAYS use when writing code importing \"@tanstack/vue-query\". Consult for debugging, best practices, or modifying @tanstack/vue-query, tanstack/vue-query, tanstack vue-query, tanstack vue query, query." license: MIT
@tanstack/vue-query@5.103.2
Async data hooks for Vue. Version 5.103.2, prepared source at input/source. Peer deps: vue ^2.6.0 || ^3.3.0; Vue 2 needs @vue/composition-api (optional peer) (package.json:56-59). Re-exports everything from @tanstack/query-core 5.103.2, so keepPreviousData, skipToken, focusManager, onlineManager, hydration utils, and observer classes import from @tanstack/vue-query directly (src/index.ts:1).
Setup
Install the plugin once per app. It creates (or accepts) a QueryClient, provides it app-wide, and mounts/unmounts it (src/vueQueryPlugin.ts:74-154):
import { createApp } from 'vue'import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query'const app = createApp(App)// Option A: pass your own client (required for SSR — one fresh client per request)const queryClient = new QueryClient()app.use(VueQueryPlugin, { queryClient })// Option B: let the plugin build one with your defaultsapp.use(VueQueryPlugin, {queryClientConfig: { defaultOptions: { queries: { staleTime: 5 * 1000 } } },})
VueQueryPluginOptions: queryClient or queryClientConfig (one or the other), queryClientKey (only when installing multiple clients), clientPersister + clientPersisterOnSuccess (persisted cache restore), enableDevtoolsV6Plugin (src/vueQueryPlugin.ts:11-38). The client is not mounted on the server (src/vueQueryPlugin.ts:87-89).
useQueryClient(id?) throws outside an injection context or without the plugin (src/useQueryClient.ts:27-44).
Basic query
<script setup lang="ts">import { ref } from 'vue'import { useQuery } from '@tanstack/vue-query'const postId = ref(1)const { status, data, error, refetch } = useQuery({queryKey: ['post', postId], // ref inside the key: tracked, no .valuequeryFn: () => fetchPost(postId.value),})</script><template><span v-if="status === 'pending'">Loading...</span><span v-else-if="status === 'error'">Error: {{ error!.message }}</span><h1 v-else>{{ data!.title }}</h1></template>
Basic mutation
<script setup lang="ts">import { useMutation, useQueryClient } from '@tanstack/vue-query'const queryClient = useQueryClient()const addMutation = useMutation({mutationFn: addTodo,onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),})function onAdd() {addMutation.mutate('Item', {onError: (error) => console.error('Failed:', error),})}</script><template><button @click="onAdd">Add</button></template>
mutate swallows rejection by design; per-call callbacks fire only for the latest call. Use mutateAsync for a promise per call (src/useMutation.ts:262-270).
Rules that catch Vue developers
- Returns are `Ref`s. Every result field of
useQuery/useInfiniteQuery/useMutationis aRef(data.valuein script, auto-unwrapped in template), except the functionsrefetch,fetchNextPage,fetchPreviousPage,mutate,mutateAsync,reset, plussuspense()(src/useBaseQuery.ts:27-40,src/useMutation.ts:53-68). - `useQueries` returns one `Ref` of the whole result array (or the
combinevalue), not a reactive array (src/useQueries.ts:357-376). - Never unwrap refs for the query key. Put the
refitself intoqueryKeyand read.valueinsidequeryFn(src/utils.ts:84-111). - Only `queryKey` and `enabled` accept reactive getters. A function anywhere else is the real callback (
queryFn,select, ...). To make any option reactive, pass a whole-options getteruseQuery(() => ({ ... }))(src/queryOptions.ts:67-76,src/useQuery.ts:225-227). - `initialData` is not reactive; it is read once (
src/queryOptions.ts:136-148). - Call composables in `setup()` or an `effectScope`. Outside a scope, dev mode warns about memory leaks (
src/useBaseQuery.ts:82-88). - Imperative fetches go through `queryClient.query()` / `queryClient.infiniteQuery()`.
fetchQuery,prefetchQuery,fetchInfiniteQuery,prefetchInfiniteQuery,ensureQueryDataare deprecated and slated for removal (src/queryClient.ts:251-253,src/queryClient.ts:311-313). - Declare shared queries with `queryOptions()` so the key carries its data type for
getQueryData,invalidateQueries, and prefetch reuse (src/queryOptions.ts:244-251).
Environment limits
- Prepared source:
@tanstack/vue-query5.103.2 with@tanstack/query-core5.103.2 (package.json:2,45). Docs match v5; v4 APIs may appear in old examples. - Vue 3.3+ for Vue 3; Vue 2.6+ with
@vue/composition-api, or Vue 2.7 native (package.json:52-59). - ESM-first dual build (
build/modern,build/legacy);sideEffects: false(package.json:21-35).
References
- references/api.md — every export, its signature, and where it lives in the source.
- references/reactivity.md — refs, getters,
skipToken,queryOptionsgetters,shallow. - references/query-client.md — Vue
QueryClient, reactive args, invalidation timing, SSR, multiple clients. - references/mutations.md — mutations,
mutationOptions,useMutationState, optimistic updates. - references/migration.md — v5 breaking changes and version-specific rules since 5.90.
Official docs: https://tanstack.com/query/latest/docs/framework/vue/overview