"ALWAYS use when writing code importing \"@pinia/colada\". Consult for debugging, best practices, or modifying @pinia/colada, pinia/colada, pinia colada, pinia-colada."
Install
npx skillscat add harlan-zw/vue-ecosystem-skills/pinia-colada-skilld Install via the SkillsCat registry.
posva/pinia-colada @pinia/colada
Version: 0.21.4 (Feb 2026)
Tags: latest: 0.21.4 (Feb 2026)
References: Docs — API reference, guides • GitHub Issues — bugs, workarounds, edge cases • GitHub Discussions — Q&A, patterns, recipes • Releases — changelog, breaking changes, new APIs
API Changes
This section documents version-specific API changes — prioritize recent major/minor releases.
BREAKING:
useInfiniteQuery()— v0.20.0 refactored: removedmerge, changeddatato{ pages, pageParams },initialPage→initialPageParam,loadMore→loadNextPage, andgetNextPageParamis now required (experimental) sourceBREAKING:
PiniaColadainstallation — v0.14.0 moved global options toqueryOptions: { ... }and requires an options object for typing:app.use(PiniaColada, {})sourceBREAKING:
useQuery()aliases —isFetchingwas renamed toisLoadingin v0.8.0 to better reflect its connection toasyncStatussourceBREAKING: Status split — v0.8.0 split
statusintostatus(data:'pending'|'success'|'error') andasyncStatus(operation:'idle'|'loading') sourceBREAKING: Mutation IDs — v0.19.0 simplified mutation IDs to incremented numbers (starting at 1).
mutationCache.get()now takes the ID, and$nsuffix is removed from keys sourceBREAKING: Cache Key structure — v0.16.0 refactored internal cache to support deeply nested objects for keys.
toCacheKeynow returns a plain string. Stricter types disallowundefinedin keys sourceBREAKING:
queryCachemethod renames —cancelQuery()was renamed tocancel()in v0.11.0, andcancelQueries()was added for multiple cancellations sourceBREAKING:
setQueryState→setEntryState— v0.9.0 renamed thisqueryCacheaction to better match its purpose sourceBREAKING: External
AbortError— v0.18.0 now surfaces external abort signals as actual errors instead of silently ignoring them sourceBREAKING:
placeholderDatatypes — v0.13.0 changedplaceholderDatato only allow returningundefined(notnull) to improve type inference sourceBREAKING: Devtools dependency — v0.21.0 removed built-in
@vue/devtools-apidependency; use@pinia/colada-devtoolsinstead sourceNEW:
useInfiniteQuery()— v0.13.5 introduced infinite scrolling support (experimental) sourceNEW:
useQueryState()— v0.17.0 added this for easier state management without the fulluseQueryreturn object sourceNEW: Global Query Hooks — v0.8.0 introduced
PiniaColadaQueryHooksPluginto manageonSuccess,onError, andonSettledsource
Also changed: serializeTreeMap replaces serialize v0.14.0 · transformError removed v0.12.0 · EntryKey replaces EntryNodeKey v0.17.0 · TResult renamed TData v0.16.0 · QueryPlugin → PiniaColada v0.8.0 · delayLoadingRef removed v0.12.0 · invalidateKeys moved to plugin v0.10.0
Best Practices
- Use the grouped
stateobject for type-safe narrowing in templates — TypeScript cannot narrow destructureddataorerrorrefs based on thestatusref due to Vue'sRefwrapper limitations source
<script setup lang="ts">
const { state } = useQuery({ key: ['user'], query: fetchUser })
</script>
<template>
<div v-if="state.status === 'success'">{{ state.data.name }}</div>
<div v-else-if="state.status === 'error'">{{ state.error.message }}</div>
</template>- Wrap shared reactive state in
defineQuery()to prevent desynchronization — regular composables recreate refs for each component instance, causing only the first component to successfully trigger key-based reactivity source
export const useFilteredTodos = defineQuery(() => {
const search = ref('')
const query = useQuery({
key: () => ['todos', { search: search.value }],
query: () => fetchTodos(search.value),
})
return { ...query, search }
})- Combine hierarchical key factories with
defineQueryOptions()for strict type safety — this enables automatic type inference inqueryCachemethods without manual type casting or string-based key typos source
export const todoOptions = defineQueryOptions((id: string) => ({
key: ['todos', id],
query: () => fetchTodo(id),
}))
// Inferred TData: queryCache.getQueryData(todoOptions('1').key)Handle side effects via
watchor global plugins instead of query options —useQueryintentionally lacksonSuccess/onErrorto prevent side-effect duplication across multiple component instances sourcePrefer
refresh()overrefetch()for standard UI updates —refresh()respectsstaleTimeand deduplicates in-flight requests, whereasrefetch()forces a network call regardless of cache status sourceUse the
metaproperty for declarative cross-cutting concerns — attach metadata to queries to drive global UI behavior (like toast messages) within thePiniaColadaQueryHooksPluginsourceVerify cache state before performing optimistic rollbacks — always check if the current cache value matches the optimistic value in
onErrorto avoid overwriting concurrent successful updates from other mutations source
onError(err, vars, { newTodo, oldTodo }) {
if (newTodo === queryCache.getQueryData(['todos'])) {
queryCache.setQueryData(['todos'], oldTodo)
}
}Use
queryCache.setEntryState()for manual status synchronization — this is the preferred way to manually update an entry as setting data toundefinedviasetQueryData()is no longer supported for state resets sourceExplicitly import
useRoutefromvue-routerin NuxtdefineQuerydefinitions — the Nuxt auto-imported version can cause unnecessary query triggers orundefinedvalues due to Suspense integration sourceUse the
enabledgetter to guard "immortal" queries in global stores — prevents queries inside Pinia stores from making invalid network requests when required reactive parameters (like route params) are absent source
const result = useQuery({
key: () => ['deck', route.params.id],
query: () => fetchDeck(route.params.id),
enabled: () => !!route.params.id,
})