**Status**: Production Ready ✅ | **Last Updated**: 2025-11-28
/plugin marketplace add secondsky/claude-skills/plugin install pinia-colada@claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/common-patterns.mdreferences/configuration.mdreferences/error-catalog.mdreferences/migration-from-tanstack-vue-query.mdreferences/setup-guide.mdStatus: Production Ready ✅ | Last Updated: 2025-11-28 Latest Version: @pinia/colada@0.17.9 | 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.
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.