From pinia-colada
Pinia Colada data fetching for Vue/Nuxt with useQuery, useMutation. Use for async state, query cache, SSR, or encountering invalidation, hydration, TanStack Vue Query migration errors.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pinia-colada:pinia-coladaThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Status**: Production Ready ✅ | **Last Updated**: 2025-11-28
Status: Production Ready ✅ | Last Updated: 2025-11-28 Latest Version: @pinia/[email protected] | Dependencies: Vue 3.5.17+, Pinia 2.2.6+ or 3.0+
For Vue Projects:
bun add @pinia/colada pinia # preferred
# or: bun add @pinia/colada pinia
For Nuxt Projects:
bun add @pinia/nuxt @pinia/colada-nuxt # install both Pinia and Pinia Colada modules
# or: bun add @pinia/nuxt @pinia/colada-nuxt
Why this matters:
For Vue Projects:
// src/main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { PiniaColada } from '@pinia/colada'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(PiniaColada, {
// Optional: Configure defaults
query: {
staleTime: 5000, // 5 seconds
gcTime: 5 * 60 * 1000, // 5 minutes (garbage collection)
refetchOnMount: true,
refetchOnWindowFocus: false,
},
})
app.mount('#app')
For Nuxt Projects:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@pinia/nuxt', // Must be before @pinia/colada-nuxt
'@pinia/colada-nuxt',
],
// Optional: Configure Pinia Colada
piniaColada: {
query: {
staleTime: 5000,
gcTime: 5 * 60 * 1000,
},
},
})
CRITICAL:
@pinia/nuxt must be listed before @pinia/colada-nuxt<script setup lang="ts">
import { useQuery } from '@pinia/colada'
interface Todo {
id: number
title: string
completed: boolean
}
async function fetchTodos(): Promise<Todo[]> {
const response = await fetch('/api/todos')
if (!response.ok) {
throw new Error('Failed to fetch todos')
}
return response.json()
}
const {
data, // Ref<Todo[] | undefined>
isPending, // Ref<boolean> - initial loading
isLoading, // Ref<boolean> - any loading (including refetch)
error, // Ref<Error | null>
refresh, // () => Promise<void> - manual refetch
} = useQuery({
key: ['todos'],
query: fetchTodos,
})
</script>
<template>
<div>
<div v-if="isPending">Loading todos...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<ul v-else-if="data">
<li v-for="todo in data" :key="todo.id">
{{ todo.title }}
</li>
</ul>
</div>
</template>
CRITICAL:
key must be an array (or getter returning array) for consistent cachingquery is the async function that fetches dataisPending is true only on initial load, isLoading includes refetches<script setup lang="ts">
import { useMutation, useQueryCache } from '@pinia/colada'
interface NewTodo {
title: string
}
async function createTodo(newTodo: NewTodo) {
const response = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newTodo),
})
if (!response.ok) throw new Error('Failed to create todo')
return response.json()
}
const queryCache = useQueryCache()
const {
mutate, // (variables: NewTodo) => Promise<void>
mutateAsync, // (variables: NewTodo) => Promise<Result>
isPending, // Ref<boolean>
error, // Ref<Error | null>
data, // Ref<Result | undefined>
} = useMutation({
mutation: createTodo,
// Invalidate todos query after mutation succeeds
async onSettled({ id }) {
await queryCache.invalidateQueries({ key: ['todos'] })
},
})
function handleAddTodo(title: string) {
mutate({ title })
}
</script>
<template>
<form @submit.prevent="handleAddTodo(newTitle)">
<input v-model="newTitle" required />
<button type="submit" :disabled="isPending">
{{ isPending ? 'Adding...' : 'Add Todo' }}
</button>
<div v-if="error">Error: {{ error.message }}</div>
</form>
</template>
Why this works:
onSettled runs after success or error, perfect for invalidationinvalidateQueries marks matching queries as stale and refetches active onesmutate is fire-and-forget, mutateAsync returns Promise for await✅ Include all variables used in query function in the key
✅ Throw errors in query/mutation functions for proper error handling
✅ Use useQueryCache() for invalidation in mutations
✅ Use isPending for initial load, isLoading for any loading state
✅ Await invalidateQueries() in onSettled when you need data fresh before continuing
✅ Use placeholderData for paginated queries to avoid flashing
✅ Snapshot cache with getQueryData before optimistic updates
✅ Return context from onMutate for rollback in onError
✅ Configure staleTime and gcTime at plugin level for app-wide defaults
✅ Use reusable composables for queries instead of inline useQuery
❌ Never use plain strings as keys - always use arrays
❌ Never return undefined from query function - throw errors instead
❌ Never mutate data.value directly - it's readonly
❌ Never forget to invalidate related queries after mutations
❌ Never use onSuccess in queries (not available, use watch instead)
❌ Never forget to await mutateAsync() - it returns a Promise
❌ Never skip cancelQueries before optimistic updates (causes race conditions)
❌ Never use getQueryData without checking for undefined
❌ Never invalidate queries in onMutate (do it in onSettled)
❌ Never hardcode URLs - use environment variables for API base URLs
This skill prevents 12 documented errors. Here are the top 5:
Error: Data doesn't update in UI after successful mutation
Prevention: Always use invalidateQueries in onSettled:
useMutation({
mutation: createTodo,
async onSettled() {
await queryCache.invalidateQueries({ key: ['todos'] })
},
})
See: references/error-catalog.md #1
Error: Optimistic update gets overwritten by in-flight request
Prevention: Always call cancelQueries in onMutate:
onMutate(id) {
cache.cancelQueries({ key: ['todos'] })
// Then do optimistic update
}
See: references/error-catalog.md #2
Error: Hydration completed but contains mismatches
Prevention: Set refetchOnMount: false for SSR queries
useQuery({
key: ['todos'],
query: fetchTodos,
refetchOnMount: false, // Prevents SSR hydration mismatch
})
See: references/error-catalog.md #3
Error: Query doesn't refetch when variable changes Prevention: Use function for reactive keys:
// ❌ Wrong - static key
key: ['todos', id.value]
// ✅ Correct - reactive key
key: () => ['todos', id.value]
See: references/error-catalog.md #4
Error: PiniaColada plugin not found or SSR errors
Prevention: Always put @pinia/nuxt first:
export default defineNuxtConfig({
modules: [
'@pinia/nuxt', // MUST be first
'@pinia/colada-nuxt', // Then Colada
],
})
See: references/error-catalog.md #10
For complete error catalog (all 12 errors): See references/error-catalog.md
Detailed guides loaded when needed:
references/setup-guide.md - Complete 8-step setup process
references/common-patterns.md - 12 common patterns
references/error-catalog.md - All 12 documented errors
references/configuration.md - Full configuration reference
references/migration-from-tanstack-vue-query.md - Migration guide
references/setup-guide.md Steps 2-4references/setup-guide.md Step 7references/setup-guide.md Step 5references/setup-guide.md Step 8references/common-patterns.md Pattern 4For complete code examples of all 5 use cases, see references/setup-guide.md and references/common-patterns.md.
Load references/setup-guide.md when:
Load references/common-patterns.md when:
Load references/error-catalog.md when:
Load references/configuration.md when:
Load references/migration-from-tanstack-vue-query.md when:
Required:
Optional:
{
"dependencies": {
"@pinia/colada": "^0.17.9",
"pinia": "^3.0.4",
"vue": "^3.5.25"
},
"devDependencies": {
"@pinia/colada-nuxt": "^0.17.9"
}
}
Version Notes:
This skill is based on production usage in multiple Vue 3 and Nuxt applications:
Use this checklist to verify your setup:
@pinia/colada and pinia (or @pinia/colada-nuxt for Nuxt)PiniaColada plugin after createPinia() (Vue) or added to modules (Nuxt)useQueryuseMutationinvalidateQueries in mutation onSettled hookisPending for initial load statestaleTime and gcTime at plugin level (optional)Questions? Issues? Check: Official docs | references/setup-guide.md (8-step process) | references/error-catalog.md (all 12 errors) | references/migration-from-tanstack-vue-query.md (migration) | GitHub
This skill provides production-ready Pinia Colada setup with zero configuration errors. All 12 common issues are documented and prevented.
Guides reception of code review feedback: verify before implementing, avoid performative agreement, push back with technical reasoning when needed.
Design banners for social media, ads, website heroes, and print with multiple art direction options and AI-generated visuals.
npx claudepluginhub midego1/claude-skills --plugin pinia-colada