Loading...
Loading...
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.
npx skill4agent add secondsky/claude-skills pinia-coladabun add @pinia/colada pinia # preferred
# or: bun add @pinia/colada piniabun add @pinia/nuxt @pinia/colada-nuxt # install both Pinia and Pinia Colada modules
# or: bun add @pinia/nuxt @pinia/colada-nuxt// 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')// 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,
},
},
})@pinia/nuxt@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>keyqueryisPendingtrueisLoading<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>onSettledinvalidateQueriesmutatemutateAsyncuseQueryCache()isPendingisLoadinginvalidateQueries()onSettledplaceholderDatagetQueryDataonMutateonErrorstaleTimegcTimedata.valueonSuccessmutateAsync()cancelQueriesgetQueryDataonMutateonSettledinvalidateQueriesonSettleduseMutation({
mutation: createTodo,
async onSettled() {
await queryCache.invalidateQueries({ key: ['todos'] })
},
})references/error-catalog.mdcancelQueriesonMutateonMutate(id) {
cache.cancelQueries({ key: ['todos'] })
// Then do optimistic update
}references/error-catalog.mdHydration completed but contains mismatchesrefetchOnMount: falseuseQuery({
key: ['todos'],
query: fetchTodos,
refetchOnMount: false, // Prevents SSR hydration mismatch
})references/error-catalog.md// ❌ Wrong - static key
key: ['todos', id.value]
// ✅ Correct - reactive key
key: () => ['todos', id.value]references/error-catalog.mdPiniaColada plugin not found@pinia/nuxtexport default defineNuxtConfig({
modules: [
'@pinia/nuxt', // MUST be first
'@pinia/colada-nuxt', // Then Colada
],
})references/error-catalog.mdreferences/error-catalog.mdreferences/setup-guide.mdreferences/common-patterns.mdreferences/error-catalog.mdreferences/configuration.mdreferences/migration-from-tanstack-vue-query.mdreferences/setup-guide.mdreferences/setup-guide.mdreferences/setup-guide.mdreferences/setup-guide.mdreferences/common-patterns.mdreferences/setup-guide.mdreferences/common-patterns.mdreferences/setup-guide.mdreferences/common-patterns.mdreferences/error-catalog.mdreferences/configuration.mdreferences/migration-from-tanstack-vue-query.md{
"dependencies": {
"@pinia/colada": "^0.17.9",
"pinia": "^3.0.4",
"vue": "^3.5.25"
},
"devDependencies": {
"@pinia/colada-nuxt": "^0.17.9"
}
}@pinia/coladapinia@pinia/colada-nuxtPiniaColadacreatePinia()useQueryuseMutationinvalidateQueriesonSettledisPendingstaleTimegcTimereferences/setup-guide.mdreferences/error-catalog.mdreferences/migration-from-tanstack-vue-query.md