Quick Start
Pinia Colada is the perfect companion to Pinia to handle async state management in your Vue applications. It wil remove the need to write boilerplate code for data fetching and transparently bring caching, deduplication, invalidation, and much more. Allowing you to focus on building the best user experience for your users.
Get a quick overview of how to use Pinia Colada in your project or if you prefer to directly play with the code you can play with Pinia Colada in this Stackblitz project.
Installation
Install Pinia Colada alongside Pinia using your favorite package manager:
npm i @pinia/colada
Setup
Install the PiniaColada
plugin after Pinia (so it picks it up automatically ✨):
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { PiniaColada } from '@pinia/colada'
const app = createApp({})
app.use(createPinia())
app.use(PiniaColada)
app.mount('#app')
Usage
Let's see a quick example that covers the basics of queries, mutations and query invalidation.
Querying
Query data with useQuery()
in any component. Always provide a key
and an async query
:
<script setup lang="ts">
import { useQuery } from '@pinia/colada'
import { getAllProducts } from '@/api/products'
import ProductItem from '@/components/ProductItem.vue'
const {
// when using multiple queries in the same component,
// it is convenient to rename `data`
data: productList,
asyncStatus,
error,
} = useQuery({
key: ['products-list'],
query: getAllProducts,
})
</script>
<template>
<main>
<LoadingIndicator v-if="asyncStatus === 'loading'" />
<div v-if="error">
<ErrorMessage :error="error" />
</div>
<div v-else-if="productList">
<div v-for="product in productList" :key="product.id">
<!-- add prefetch here : @mouseover="prefetch(product.id)" -->
<ProductItem :product="product" />
</div>
</div>
</main>
</template>
<script setup lang="ts">
import { useQuery } from '@pinia/colada'
import { useRoute } from 'vue-router'
import { getProductById } from '@/api/products'
import ProductItemDetail from '@/components/ProductItemDetail.vue'
// in this example we use the url params in the query to fetch
// a specific product
const route = useRoute()
const {
data: product,
asyncStatus,
error,
} = useQuery({
key: () => ['products', route.params.id as string],
query: () => getProductById(route.params.id as string),
})
</script>
<template>
<main>
<LoadingIndicator v-if="asyncStatus === 'loading'" />
<div v-if="error">
<ErrorMessage :error="error" />
</div>
<div v-else class="flex flex-wrap">
<ProductItemDetail :product="product" />
</div>
</main>
</template>
Mutating data
Mutate data with useMutation()
in any component. Unlike queries, mutations only require a mutation
function, the key
is optional:
<script setup lang="ts">
import { useMutation } from '@pinia/colada'
import { type Contact, updateContact } from '@/api/contacts'
import ContactDetail from '@/components/ContactDetail.vue'
const props = defineProps<{ contact: Contact }>()
const {
// we use the async version of the mutation so ContactDetail
// can use the asyncStatus to disable a form
mutateAsync,
asyncStatus,
error,
} = useMutation({
mutation: (contact: Contact) => updateContact(contact),
})
</script>
<template>
<main>
<LoadingIndicator v-if="asyncStatus === 'loading'" />
<div v-if="error">
<ErrorMessage :error="error" />
</div>
<div v-else>
<ContactDetail :contact="props.contact" @update="mutateAsync" />
</div>
</main>
</template>
Invalidated queries will be refetched automatically if they are actively being used ✨.
Going further
Pinia Colada has much more to offer! It can greatly improves your application's performance and user experience. Check out the following guides to learn more: