Vue · Pinia

Vue 3 Pinia State Management

Pinia is the official Vue state library and the successor to Vuex. It's lighter, TypeScript-friendly, and simple enough that the main skill is knowing what belongs in a store.

John Kihiu12 min read

Pinia is Vue's official state-management library and the recommended replacement for Vuex. It exists for shared application state — data that many components need and that outlives any single component. It is deliberately simpler than Vuex (no mutations, less ceremony) and works naturally with the Composition API and TypeScript, so the real skill is less about Pinia's API and more about deciding what state actually belongs in a store.

Defining a store

A Pinia store has state, getters (derived state, like computed), and actions (methods that change state). The setup-store syntax mirrors <script setup> and is the most natural for Composition API users:

JavaScript · a Pinia setup store
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCartStore = defineStore('cart', () => {
  const items = ref([])
  const total = computed(() => items.value.reduce((s, i) => s + i.price, 0))
  function add(item) { items.value.push(item) }
  return { items, total, add }
})

In a component you call useCartStore() and use its state, getters, and actions directly. Note the absence of Vuex's mutations — in Pinia you change state in actions (or directly), which removes a whole layer of boilerplate that never earned its keep.

What belongs in a store

The most common Pinia mistake is putting too much in stores — treating them as a dumping ground for all state. The guideline: a store is for state genuinely shared across components or that must persist beyond one component's lifecycle — the current user, a shopping cart, app-wide settings. State used by a single component and its children belongs in that component (via ref/props), not a global store. Over-centralising state makes the app harder to reason about, not easier.

Keep stores focused

Split state into multiple small, focused stores by domain — a cart store, a user store, a settings store — rather than one giant store. Pinia is designed for this; stores can use each other where needed. Small stores are easier to understand, test, and maintain, and because each is just a function returning reactive state, testing a store is as simple as calling it and asserting on the result.

Not everything is global state

Reaching for a store by default leads to a bloated global state that couples unrelated parts of the app. Ask whether the state is truly shared before it goes in Pinia — most component state is local and should stay that way. Global state is powerful and, precisely because of that, worth using sparingly.

Pinia gives Vue 3 clean, TypeScript-friendly state management with less ceremony than Vuex: focused stores of state, getters, and actions, used directly in components. The API is the easy part; the discipline is reserving stores for genuinely shared state and keeping them small and domain-focused. Use it for what is actually global, keep the rest local, and your app's state stays understandable as it grows.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.