This episode dissects the anatomy of a Pinia store: state for reactive data, getters for derived state, and actions for mutations and side effects. You also understand how Pinia works behind the scenes, the createPinia and app.use installation, and the difference between Options stores and Setup stores.

Every state management framework has core concepts. In Pinia, that concept is called a store: a reactive entity that holds data, derived logic, and actions together in a single unit. Understanding the anatomy of a store is the key to understanding this entire series.
Episode 2 dissects Pinia's main architecture: the three parts that make up a store, how Pinia works behind the scenes, and the two styles of writing stores. You don't need to write complex stores yet — this episode builds a shared vocabulary so the following episodes run smoothly.
state is a store's source of truth. It's defined as a function that returns an object, so every store instance gets truly fresh data:
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
title: 'Counter App',
}),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})defineStore('counter', ...) takes a unique id as its first argument — this id is what distinguishes one store from another within the app.
getters are the store's computed properties. Their values are calculated from state and automatically updated when state changes. Because they're computed, their results are memoized as long as their dependencies don't change.
actions are methods that may change state, call APIs, set timers, and call other stores. Inside an action, this points to the store instance, so you can access state and other getters.
When a store is first used, Pinia creates that store as a reactive object inside the Pinia instance. As a result, accessing store.count in a component tracks the dependency just like a regular ref — when the value changes, the consuming components re-render along with it.
Because a store is a reactive object, ordinary destructuring destroys reactivity. Pinia provides storeToRefs(store) to extract state and getters as refs — we'll cover it thoroughly in episode 5. On the other hand, Pinia's plugin system lets you add properties to all stores globally, such as localStorage sync or a logger.
Pinia is installed into the app in two steps: create an instance with createPinia(), then register it with app.use(pinia). In apps with more than one Pinia instance — for example during SSR — each request gets its own instance:
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')createPinia() creates the instance, and app.use(pinia) activates it for the entire app. After this, stores can be called from anywhere inside components.
Pinia offers two ways of writing stores:
state, getters, actions — close to Vuex, comfortable for teams that just migrated.refs, computeds, and functions — more flexible because you can use other composables inside the store.Both styles write the same thing in different shapes:
// Options store
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: { double: (state) => state.count * 2 },
actions: { increment() { this.count++ } },
})
// Setup store
export const useCounterStoreSetup = defineStore('counter-setup', () => {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, double, increment }
})Notice the difference: in the Options store, this.count++ is used because state and getters are available as properties. In the Setup store, you use ref and computed directly as you would inside a component — there's no this, and the results are returned explicitly. Both forms access store.count, store.double, and store.increment() the same way from components.
Both have equal capabilities; the choice is a matter of taste and team convention. The details are compared directly in episode 4.
Warning
A store must have a unique id. If two stores use the same id, Pinia will throw a warning and one of them will overwrite the other in DevTools.
Episode 2 gives you the map of Pinia's architecture. You now understand the three parts that make up a store — state, getters, actions — how stores work as reactive objects, installation via createPinia and app.use, and the two styles of writing stores.
Key takeaways:
state (data), getters (derived values), and actions (mutations plus side effects).defineStore needs a unique id as its first argument.reactive object, so accessing its properties is tracked automatically.storeToRefs is required when destructuring so reactivity isn't lost.createPinia() then app.use(pinia).In the next episode, episode 3, we'll do the installation setup and create the first store — install pinia, register it with the app, define a counter store with an increment action, and use it directly inside a Vue component. It's time to write real code.