Pinia provides two equally capable styles of writing stores: the Options store with state, getters, and actions, and the Setup store based on the Composition API. This episode writes the same store in both styles, then discusses when to choose each one and the advantage of Setup stores in using composables.

Pinia has one store concept, but two ways of writing it: the Options store and the Setup store. Both are equal in capabilities — state, getters, actions, accessing other stores, even SSR — so the choice is more about conventions and team needs.
Episode 4 will write the exact same store in both styles, dissect the syntax differences, and then discuss the advantage of Setup stores in using external composables. After this episode, you'll be fluent in reading Pinia code in any form.
The Options store is the classic style: an object with state, getters, and actions properties. This style is very close to Vuex, so teams migrating from Vuex will feel right at home:
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: 'Arman',
age: 25,
}),
getters: {
isAdult: (state) => state.age >= 18,
greeting: (state) => `Hello, ${state.name}!`,
},
actions: {
celebrate() {
this.age++
},
},
})In the Options store, getters receive state as their first parameter, and actions access everything through this. defineStore('user', ...) is still required with a unique id, and the end result is identical to the Setup store.
The Setup store is written as a Composition API function. State becomes refs, getters become computeds, and actions become plain functions:
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
export const useUserStore = defineStore('user', () => {
const name = ref('Arman')
const age = ref(25)
const isAdult = computed(() => age.value >= 18)
const greeting = computed(() => `Hello, ${name.value}!`)
function celebrate() {
age.value++
}
return { name, age, isAdult, greeting, celebrate }
})Notice the pattern: all refs, computeds, and functions are returned in a single object. Inside the template, reactivity works through .value in code, but it's automatically unwrapped when accessed from the store instance.
The main advantage of the Setup store is the ability to use other composables inside it. The composables you write for components can be used directly in the store:
import { defineStore } from 'pinia'
import { useLocalStorage } from '@vueuse/core'
export const useSettingsStore = defineStore('settings', () => {
const theme = useLocalStorage('theme', 'light')
function toggleTheme() {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
return { theme, toggleTheme }
})useLocalStorage('theme', 'light') from VueUse can't be used in an Options store without tricks, but in a Setup store it runs naturally because the store function executes like a regular composable.
There's no absolute answer, but there is guidance teams often use:
Info
In DevTools, both styles look identical — the Pinia tab shows the same state, getters, and actions. So the style decision doesn't affect tooling at all.
Episode 4 shows that the Options store and the Setup store are two faces of the same concept. You can now write a store in both styles, understand the Setup store's advantage in using composables, and choose the style that fits your team's context.
Key takeaways:
state, getters, actions.ref, computed, and plain functions.this; Setup stores use closures.In the next episode, episode 5, we'll discuss state and reactivity — accessing state directly, updating many fields at once with $patch, resetting a store with $reset, and keeping reactivity when destructuring with storeToRefs. This is important groundwork for understanding getters and actions in episodes 6 and 7.