Besides Option Stores you can also use Setup Stores to define stores in Pinia. They bring more flexibility as you can create watchers within a store and freely use any composable.
Defining Setup Store
Just like the setup function in the Vue Composition API, a function that sets up reactive properties and methods can be passed, which will return an object containing the properties and methods that should be made available:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
In Setup Stores:
ref()s becomestatepropertiescomputed()s becomegettersfunction()s becomeactions
Using Setup Store
At this point, we only defined the store but it won't be created until use...Store() is called inside of setup():
<script setup>
const store = useCounterStore()
</script>
<template>
<span>Counter: {{ store.count }}</span>
<span>Double Count: {{ store.doubleCount }}</span>
<button @click="store.increment">Increment</button>
</template>
store object has been wrapped with reactive, so there is no need to include .value when using getters, but similar to props in setup, it cannot be destructured. You need to use storeToRefs() to destructure while keeping reactivity.Bonus Tip
You can use composables that return writable state like VueUse useLocalStorage() directly within the state() function:
export const useCounterStore = defineStore('counter', () => {
const counterInfo = useLocalStorage('counterInfo', null)
return { counterInfo }
})
The same example using Option Store:
export const useCounterStore = defineStore('counter', () => {
state: () => ({
// ...
counterInfo: useLocalStorage('counterInfo', null),
})
})
If you liked this Vue tip, follow me on BlueSky to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter :

