Vue

3 / 10

Vue

State Management with Pinia: Less Than You Think

Most Vue state does not belong in a store. When component state and composables suffice, and how to structure Pinia when you genuinely need it.

The most common Pinia mistake is using it too much. Global stores full of things only one component reads are Vuex habits wearing new clothes. The modern Vue rule of thumb: state lives as locally as possible, and it earns promotion to a store only by being genuinely shared.

The escalation ladder

  • Component state (ref in setup) for anything one component owns: form inputs, toggles, local UI.
  • Props and events for parent-child coordination; provide/inject for a subtree.
  • Composables with module-level state for lightweight sharing without store ceremony.
  • Pinia when state is read and written across distant parts of the app: the cart, the session, feature flags, notifications.

When you do reach for Pinia

Prefer the setup-store syntax, where a store is just a composable with refs and functions returned; it matches everything else you write. Keep stores small and domain-shaped, a useCartStore, a useSessionStore, rather than one grand app store. Getters are computeds; actions are where mutations and async work live, which keeps devtools traces meaningful.

Server state is not app state

The data you fetched from an API is a cache of someone else's truth, and caches want tooling: deduplication, refresh, invalidation. In Nuxt, useFetch and useAsyncData already handle that layer, so copying their results into Pinia usually adds a second source of truth that drifts. Store the client's own state, selections, drafts, UI, and let the data layer own the server's.

A useful audit: for each thing in your stores, ask who besides the original component actually reads this. The honest answers usually shrink the store by half, and the app gets easier to reason about, not harder.