posva/pinia-colada @pinia/colada
The smart data fetching layer for Vue.js
Version: 1.0.0 (Mar 2026) Tags: latest: 1.0.0 (Mar 2026)
References: Docs — API reference, guides
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) source -
BREAKING:
PiniaColadainstallation — v0.14.0 moved global options toqueryOptions: { ... }and requires an options object for typing:app.use(PiniaColada, {})source -
BREAKING:
useQuery()aliases —isFetchingwas renamed toisLoadingin v0.8.0 to better reflect its connection toasyncStatussource -
BREAKING: Status split — v0.8.0 split
statusintostatus(data:'pending'|'success'|'error') andasyncStatus(operation:'idle'|'loading') source -
BREAKING: 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 source -
BREAKING: 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 source -
BREAKING:
queryCachemethod renames —cancelQuery()was renamed tocancel()in v0.11.0, andcancelQueries()was added for multiple cancellations source -
BREAKING:
setQueryState→setEntryState— v0.9.0 renamed thisqueryCacheaction to better match its purpose source -
BREAKING: External
AbortError— v0.18.0 now surfaces external abort signals as actual errors instead of silently ignoring them source -
BREAKING:
placeholderDatatypes — v0.13.0 changedplaceholderDatato only allow returningundefined(notnull) to improve type inference source -
BREAKING: Devtools dependency — v0.21.0 removed built-in
@vue/devtools-apidependency; use@pinia/colada-devtoolsinstead source -
NEW:
useInfiniteQuery()— v0.13.5 introduced infinite scrolling support (experimental) source -
NEW:
useQueryState()— v0.17.0 added this for easier state management without the fulluseQueryreturn object source -
NEW: 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 source -
Prefer
refresh()overrefetch()for standard UI updates —refresh()respectsstaleTimeand deduplicates in-flight requests, whereasrefetch()forces a network call regardless of cache status source -
Use the
metaproperty for declarative cross-cutting concerns — attach metadata to queries to drive global UI behavior (like toast messages) within thePiniaColadaQueryHooksPluginsource -
Verify 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 source -
Explicitly import
useRoutefromvue-routerin NuxtdefineQuerydefinitions — the Nuxt auto-imported version can cause unnecessary query triggers orundefinedvalues due to Suspense integration source -
Use 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,
})