Utilities
Pinia provides several utility functions to help you work with stores more effectively in different scenarios.
storeToRefs()
Extracts refs from a store, making state and getters reactive while preserving their reactivity.
Signature
function storeToRefs<T extends StoreGeneric>(
store: T
): StoreToRefs<T>
Parameters
- store: The store instance to extract refs from
Returns
An object containing reactive refs for all state properties and getters.
Example
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">+</button>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
// Extract reactive refs
const { count, doubleCount } = storeToRefs(store)
// Actions can be destructured directly
const { increment } = store
</script>
Why Use storeToRefs?
Without storeToRefs
, destructured properties lose reactivity:
// ❌ This breaks reactivity
const { count, doubleCount } = store
// ✅ This preserves reactivity
const { count, doubleCount } = storeToRefs(store)
TypeScript
import type { StoreToRefs } from 'pinia'
type CounterRefs = StoreToRefs<ReturnType<typeof useCounterStore>>
// CounterRefs = {
// count: Ref<number>
// doubleCount: ComputedRef<number>
// }
mapStores()
Maps multiple stores to computed properties for use in Options API components.
Signature
function mapStores<T extends Record<string, StoreDefinition>>(
...stores: [...StoreDefinitions<T>]
): ComputedOptions
Parameters
- stores: Store definitions to map
Returns
Computed properties object for Options API.
Example
import { mapStores } from 'pinia'
import { useUserStore } from '@/stores/user'
import { useCartStore } from '@/stores/cart'
export default {
computed: {
// Maps to this.userStore and this.cartStore
...mapStores(useUserStore, useCartStore)
},
methods: {
async login() {
await this.userStore.login()
this.cartStore.loadUserCart()
}
}
}
Custom Names
import { mapStores } from 'pinia'
import { useUserStore } from '@/stores/user'
export default {
computed: {
// Maps to this.user instead of this.userStore
...mapStores({ user: useUserStore })
}
}
setActivePinia()
Sets the active Pinia instance. Useful for testing and SSR.
Signature
function setActivePinia(pinia: Pinia | undefined): Pinia | undefined
Parameters
- pinia: The Pinia instance to set as active
Returns
The previously active Pinia instance.
Example
import { createPinia, setActivePinia } from 'pinia'
// Testing setup
const pinia = createPinia()
setActivePinia(pinia)
// Now stores can be used without app context
const store = useMyStore()
Testing Usage
import { beforeEach } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
beforeEach(() => {
setActivePinia(createPinia())
})
test('store works', () => {
const store = useMyStore()
expect(store.count).toBe(0)
})
getActivePinia()
Gets the currently active Pinia instance.
Signature
function getActivePinia(): Pinia | undefined
Returns
The currently active Pinia instance, or undefined
if none is set.
Example
import { getActivePinia } from 'pinia'
function myPlugin() {
const pinia = getActivePinia()
if (pinia) {
// Use pinia instance
}
}
acceptHMRUpdate()
Enables Hot Module Replacement (HMR) for stores during development.
Signature
function acceptHMRUpdate<T>(
initialUseStore: () => T,
hot: any
): (newModule: any) => any
Parameters
- initialUseStore: The store composable function
- hot: The HMR context (usually
import.meta.hot
)
Example
// stores/counter.js
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
}
}
})
// Enable HMR
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot))
}
Vite Configuration
// vite.config.js
export default {
define: {
__VUE_PROD_DEVTOOLS__: true
}
}
skipHydration()
Marks a ref to be skipped during SSR hydration.
Signature
function skipHydration<T>(obj: T): T
Parameters
- obj: The ref or reactive object to skip hydration for
Example
import { defineStore, skipHydration } from 'pinia'
import { ref } from 'vue'
export const useStore = defineStore('main', () => {
// This will not be hydrated from server state
const clientOnlyData = skipHydration(ref('client-only'))
return { clientOnlyData }
})
Use Cases
- Client-specific data (user preferences, device info)
- Data that should always be fresh on client
- Avoiding hydration mismatches
Helper Functions
isRef()
Checks if a value is a ref.
import { isRef } from 'vue'
import { storeToRefs } from 'pinia'
const store = useMyStore()
const { count } = storeToRefs(store)
console.log(isRef(count)) // true
console.log(isRef(store.count)) // false
unref()
Gets the value of a ref or returns the value if it's not a ref.
import { unref } from 'vue'
import { storeToRefs } from 'pinia'
const store = useMyStore()
const { count } = storeToRefs(store)
console.log(unref(count)) // actual number value
toRef()
Creates a ref for a property on a reactive object.
import { toRef } from 'vue'
const store = useMyStore()
const countRef = toRef(store, 'count')
// countRef is reactive and linked to store.count
Advanced Patterns
Conditional Store Access
import { getActivePinia } from 'pinia'
function useStoreIfAvailable() {
const pinia = getActivePinia()
if (pinia) {
return useMyStore()
}
return null
}
Store Factory
import { setActivePinia, createPinia } from 'pinia'
function createStoreInstance() {
const pinia = createPinia()
setActivePinia(pinia)
return useMyStore()
}
Reactive Store List
import { computed } from 'vue'
import { storeToRefs } from 'pinia'
function useMultipleStores(storeList) {
return computed(() => {
return storeList.map(useStore => {
const store = useStore()
return storeToRefs(store)
})
})
}
TypeScript Utilities
Store Type Extraction
import type { Store } from 'pinia'
type MyStore = ReturnType<typeof useMyStore>
type MyStoreState = MyStore['$state']
type MyStoreGetters = Pick<MyStore, 'doubleCount' | 'isEven'>
Generic Store Helper
function createStoreHelper<T extends StoreGeneric>(useStore: () => T) {
return {
getRefs: () => storeToRefs(useStore()),
getInstance: () => useStore(),
reset: () => useStore().$reset()
}
}
const counterHelper = createStoreHelper(useCounterStore)
Best Practices
1. Always Use storeToRefs for Destructuring
// ✅ Good
const { count, name } = storeToRefs(store)
const { increment, updateName } = store
// ❌ Bad - loses reactivity
const { count, name, increment } = store
2. Prefer Composition API
// ✅ Preferred
const store = useMyStore()
const { count } = storeToRefs(store)
// ⚠️ Options API when necessary
computed: {
...mapStores(useMyStore)
}
3. Use setActivePinia in Tests
// ✅ Good test setup
beforeEach(() => {
setActivePinia(createPinia())
})
// ❌ Don't forget to set active pinia
test('store test', () => {
const store = useMyStore() // May fail without active pinia
})
4. Enable HMR in Development
// ✅ Always add HMR support
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useMyStore, import.meta.hot))
}
See Also
- Store Instance - Store instance API
- storeToRefs - Detailed storeToRefs guide
- Map Helpers - Mapping utilities
- Testing Guide - Testing with Pinia