Skip to content

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 ​

ts
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 ​

vue
<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:

js
// ❌ This breaks reactivity
const { count, doubleCount } = store

// ✅ This preserves reactivity
const { count, doubleCount } = storeToRefs(store)

TypeScript ​

ts
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 ​

ts
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 ​

js
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 ​

js
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 ​

ts
function setActivePinia(pinia: Pinia | undefined): Pinia | undefined

Parameters ​

  • pinia: The Pinia instance to set as active

Returns ​

The previously active Pinia instance.

Example ​

js
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 ​

js
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 ​

ts
function getActivePinia(): Pinia | undefined

Returns ​

The currently active Pinia instance, or undefined if none is set.

Example ​

js
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 ​

ts
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 ​

js
// 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 ​

js
// vite.config.js
export default {
  define: {
    __VUE_PROD_DEVTOOLS__: true
  }
}

skipHydration() ​

Marks a ref to be skipped during SSR hydration.

Signature ​

ts
function skipHydration<T>(obj: T): T

Parameters ​

  • obj: The ref or reactive object to skip hydration for

Example ​

js
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.

js
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.

js
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.

js
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 ​

js
import { getActivePinia } from 'pinia'

function useStoreIfAvailable() {
  const pinia = getActivePinia()
  if (pinia) {
    return useMyStore()
  }
  return null
}

Store Factory ​

js
import { setActivePinia, createPinia } from 'pinia'

function createStoreInstance() {
  const pinia = createPinia()
  setActivePinia(pinia)
  return useMyStore()
}

Reactive Store List ​

js
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 ​

ts
import type { Store } from 'pinia'

type MyStore = ReturnType<typeof useMyStore>
type MyStoreState = MyStore['$state']
type MyStoreGetters = Pick<MyStore, 'doubleCount' | 'isEven'>

Generic Store Helper ​

ts
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 ​

js
// ✅ Good
const { count, name } = storeToRefs(store)
const { increment, updateName } = store

// ❌ Bad - loses reactivity
const { count, name, increment } = store

2. Prefer Composition API ​

js
// ✅ Preferred
const store = useMyStore()
const { count } = storeToRefs(store)

// ⚠️ Options API when necessary
computed: {
  ...mapStores(useMyStore)
}

3. Use setActivePinia in Tests ​

js
// ✅ 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 ​

js
// ✅ Always add HMR support
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useMyStore, import.meta.hot))
}

See Also ​

Released under the MIT License.