mcpField
T
tanstack-vue-query-skilld

tanstack-vue-query-skilld

github.com/skilld-devDevelopment184216 installsUpdated 3w ago
Repository

About tanstack-vue-query-skilld

tanstack-vue-query-skilld is a skill reference for Vue developers working with asynchronous data, based on @tanstack/vue-query 5.103.2. It explains queries, mutations, the query client and Vue reactivity rules.

Key features

  1. 01Async query and mutation composables
  2. 02Shared QueryClient configuration
  3. 03Cache invalidation and data updates
  4. 04Reactive query keys and options
  5. 05Query state and data typing

Use cases

  1. 01Read asynchronous data in Vue components
  2. 02Invalidate related cache after a mutation
  3. 03Refresh queries from reactive parameters
  4. 04Configure and reuse a query client in an app

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/skilld-dev/vue-ecosystem-skills --skill tanstack-vue-query-skilld

After installing, type/tanstack-vue-query-skilldin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
api.md
migration.md
mutations.md
query-client.md
reactivity.md

Details

From the project README

@tanstack/[email protected]

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 defaults
app.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 .value
  queryFn: () => 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 Refs. Every result field of useQuery/useInfiniteQuery/useMutation is a Ref (data.value in script, auto-unwrapped in template), except the functions refetch, fetchNextPage, fetchPreviousPage, mutate, mutateAsync, reset, plus suspense() (src/useBaseQuery.ts:27-40, src/useMutation.ts:53-68).
  • useQueries returns one Ref of the whole result array (or the combine value), not a reactive array (src/useQueries.ts:357-376).
  • Never unwrap refs for the query key. Put the ref itself into queryKey and read .value inside queryFn (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 getter useQuery(() => ({ ... })) (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, ensureQueryData are 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-query 5.103.2 with @tanstack/query-core 5.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

Official docs: https://tanstack.com/query/latest/docs/framework/vue/overview

Comments

Markdown supported