Pinia
Pinia is the standard state management library for Vue 3, replacing Vuex. A store is a function returning reactive state, computed getters, and actions — very close to a composable, with the difference that a store is a singleton shared across the application.
Compared with Vuex it removes mutations entirely. Vuex required state changes to go through a mutation, separate from the action containing the logic, which meant writing two things for every change. Pinia lets actions modify state directly, and the ceremony that removes turns out not to have been buying much.
What it is actually for
Not “state management” in the abstract — Vue’s reactivity already manages state. Pinia is for state that is shared between components with no useful relationship to each other.
The authenticated user is the clearest case: the navbar needs it, route guards need it, half the views need it, and passing it down through props means threading it through components that do not care. Same for anything cached from the API that several unrelated views display.
Anything else should stay local. A form’s contents belong to the form; a modal’s open state belongs to whatever opens it. Putting them in a store makes them global, which means any component can change them and reasoning about who did becomes harder.
I reach for a store too early more often than too late. Local state that turns out to be needed elsewhere is easy to lift; global state that turns out to be local is rarely put back.
Two things worth knowing
Destructuring breaks reactivity. const { user } = useUserStore() gives a value disconnected from the store. storeToRefs is the fix, and this is the same Vue reactivity trap in store clothing — and the most common Pinia bug by a wide margin.
Stores persist for the application’s lifetime. In a single-page application a store holds its data across route changes, which is usually what you want and occasionally not — stale data from a previous context showing up in a new one. Resetting on logout matters more than it sounds, because a leftover store is how one user’s data appears to the next.
Server-side rendering with Nuxt complicates this further, since a singleton on the server would be shared between requests. Nuxt handles it; it is worth knowing why the handling exists.
See also: Vue 3, Nuxt 3, and Status Alganize.