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.
What this skill does
# Pinia Colada - Smart Data Fetching for Vue **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+ --- ## Quick Start (5 Minutes) ### 1. Install Dependencies **For Vue Projects:** ```bash bun add @pinia/colada pinia # preferred # or: bun add @pinia/colada pinia ``` **For Nuxt Projects:** ```bash 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:** - Pinia Colada requires Pinia 2.2.6+ or 3.0+ as peer dependency - Nuxt module handles SSR serialization automatically - Vue 3.5.17+ required for optimal reactivity ### 2. Set Up Pinia Colada Plugin **For Vue Projects:** ```typescript // 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:** ```typescript // 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:** - For Nuxt: `@pinia/nuxt` must be listed before `@pinia/colada-nuxt` - Plugin must be registered after Pinia instance - Configuration is optional - sensible defaults provided ### 3. Create First Query ```vue <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:** - Query `key` must be an array (or getter returning array) for consistent caching - Query `query` is the async function that fetches data - Throw errors in query function for proper error handling - `isPending` is `true` only on initial load, `isLoading` includes refetches ### 4. Create First Mutation ```vue <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 invalidation - `invalidateQueries` marks matching queries as stale and refetches active ones - `mutate` is fire-and-forget, `mutateAsync` returns Promise for await - Mutations don't cache by default (correct behavior for writes) --- ## Critical Rules ### Always Do ✅ 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 Do ❌ 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 --- ## Top 5 Errors Prevention This skill prevents **12 documented errors**. Here are the top 5: ### Error #1: Query Not Refetching After Mutation **Error**: Data doesn't update in UI after successful mutation **Prevention**: Always use `invalidateQueries` in `onSettled`: ```typescript useMutation({ mutation: createTodo, async onSettled() { await queryCache.invalidateQueries({ key: ['todos'] }) }, }) ``` **See**: `references/error-catalog.md` #1 ### Error #2: Race Condition with Optimistic Updates **Error**: Optimistic update gets overwritten by in-flight request **Prevention**: Always call `cancelQueries` in `onMutate`: ```typescript onMutate(id) { cache.cancelQueries({ key: ['todos'] }) // Then do optimistic update } ``` **See**: `references/error-catalog.md` #2 ### Error #3: SSR Hydration Mismatch **Error**: `Hydration completed but contains mismatches` **Prevention**: Set `refetchOnMount: false` for SSR queries ```typescript useQuery({ key: ['todos'], query: fetchTodos, refetchOnMount: false, // Prevents SSR hydration mismatch }) ``` **See**: `references/error-catalog.md` #3 ### Error #4: Query Key Not Reactive **Error**: Query doesn't refetch when variable changes **Prevention**: Use function for reactive keys: ```typescript // ❌ Wrong - static key key: ['todos', id.value] // ✅ Correct - reactive key key: () => ['todos', id.value] ``` **See**: `references/error-catalog.md` #4 ### Error #5: Nuxt Module Order Wrong **Error**: `PiniaColada plugin not found` or SSR errors **Prevention**: Always put `@pinia/nuxt` first: ```typescript 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` --- ## Using Bundled Resources ### References (references/) Detailed guides loaded when needed: - **`referenc
Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.