web-state-pinia
Pinia State Management Patterns
Quick Guide: Use Pinia for all shared client state in Vue 3. Options stores for simplicity, Setup stores for flexibility. Server data? Use your data fetching solution. Use
storeToRefs()when destructuring state.
<critical_requirements>
CRITICAL: Before Managing State with Pinia
(You MUST use a data fetching solution for ALL server/API data - NEVER put API responses in Pinia stores)
(You MUST use storeToRefs() when destructuring state from stores - direct destructuring loses reactivity)
(You MUST return ALL state properties in Setup stores - private state breaks SSR and DevTools)
(You MUST use named exports ONLY - NO default exports in any store files)
(You MUST use named constants for ALL numbers - NO magic numbers in state code)
</critical_requirements>
Auto-detection: Pinia, defineStore, Vue 3 state, storeToRefs, Vue state management, Options store, Setup store
When to use:
- Managing shared client state in Vue 3 applications
- Choosing between Options stores and Setup stores
- Composing stores that depend on each other
- Implementing state persistence across sessions
- Adding DevTools integration for debugging
- Setting up stores for SSR applications
When NOT to use:
- Server/API data (use a dedicated data fetching solution)
- Simple component-local state (use
ref()orreactive()) - URL-appropriate state like filters (use route query params)
Philosophy
Pinia is the official state management solution for Vue 3, designed to be intuitive, type-safe, and flexible. It eliminates the boilerplate of Vuex while maintaining powerful features like DevTools integration, plugin support, and SSR compatibility.
Core Principles:
- Modular by Default - Each store is independent, no nested modules
- No Mutations - Actions handle all state changes directly
- Full TypeScript Support - Type inference works out of the box
- Composition API Friendly - Works seamlessly with
<script setup>
State Ownership:
| State Type | Solution | Reason |
|---|---|---|
| Server/API data | Data fetching solution | Caching, synchronization, loading states |
| Shared client state | Pinia | Reactivity, DevTools, persistence |
| Component-local state | ref() / reactive() |
Simpler, no overhead |
| URL state (filters) | Route query params | Shareable, bookmarkable |
Core Patterns
Pattern 1: Options Store vs Setup Store Decision
Pinia offers two store definition syntaxes. Choose based on your needs.
Decision Tree
Which store syntax should I use?
Need composables (useRoute, useI18n)?
├─ YES → Setup Store
└─ NO → Need watchers inside store?
├─ YES → Setup Store
└─ NO → Prefer Vue Options API style?
├─ YES → Options Store
└─ NO → Setup Store (more flexible)
Options Store: Simpler, familiar to Vuex users, built-in $reset() method
Setup Store: More flexible, supports composables, requires manual reset
For implementation examples, see examples/core.md.
Pattern 2: Options Store Definition
Use Options stores for straightforward state management with familiar syntax.
When to Use
- Teams familiar with Vuex or Vue Options API
- Simple stores without composable dependencies
- When built-in
$reset()method is needed - Standard CRUD operations on client state
For implementation examples and good/bad comparisons, see examples/core.md.
Pattern 3: Setup Store Definition
Use Setup stores when you need maximum flexibility and composable integration.
When to Use
- Using Vue Router composables (
useRoute,useRouter) - Using i18n composables (
useI18n) - Need watchers inside the store
- Complex computed dependencies
- Integrating with external composables
Critical Requirements for Setup Stores
- Return ALL state properties (no private state)
- Use
ref()for state,computed()for getters - Functions become actions automatically
- Must implement custom
$reset()if needed
For implementation examples, see examples/core.md.
Pattern 4: Accessing Store State in Components
Proper store access patterns prevent reactivity issues.
Key Rules
- Never destructure state directly - loses reactivity
- Use
storeToRefs()for state/getters - preserves reactivity - Destructure actions directly - they don't need reactivity
For implementation examples with storeToRefs, see examples/core.md.
Pattern 5: Composing Stores
Stores can use other stores, but follow rules to avoid circular dependencies.
Guidelines
- Import and use stores at the top of your store function
- Avoid circular dependencies through getters/actions
- If stores reference each other, ensure no infinite loops
- Use setup stores for complex composition patterns
For implementation examples, see examples/core.md.
Pattern 6: State Persistence
Use plugins for persisting state across sessions.
Persistence Guidelines
- Use
pinia-plugin-persistedstatefor most cases - Only persist user preferences and non-sensitive data
- Use
pickoption to persist specific properties only - Never persist server data (refetch on load instead)
- Consider storage type: localStorage vs sessionStorage
For implementation examples, see examples/persistence.md.
Pattern 7: Pinia Plugins
Extend all stores with shared functionality.
Common Plugin Use Cases
- Adding shared properties (router, analytics)
- Wrapping actions with logging/timing
- Adding custom options (debounce, throttle)
- Implementing side effects (localStorage)
For implementation examples, see examples/plugins.md.
Pattern 8: Testing Stores
Test stores in isolation and within components.
Testing Approaches
- Unit tests: Test store actions/getters directly with
setActivePinia() - Component tests: Use
createTestingPinia()for component mounting - Mocking: Actions are stubbed by default with
@pinia/testing
For implementation examples, see examples/testing.md.
Pattern 9: SSR Considerations
Handle server-side rendering and hydration properly.
SSR Guidelines
- Use SSR-safe defaults (no browser APIs in initial state)
- Guard client-only logic with
typeof window !== "undefined"checks - Be careful with Setup stores - ensure all refs are returned
- Sanitize serialized state to prevent XSS
For implementation examples, see examples/ssr.md.
<red_flags>
RED FLAGS
High Priority Issues:
- Storing server/API data in Pinia instead of using a data fetching solution - causes stale data, no caching, manual sync
- Destructuring state without
storeToRefs()- silently breaks reactivity, UI shows stale data - Private state in Setup stores (not returning all refs) - breaks SSR hydration, DevTools, and plugins
- Browser APIs in state initialization (
localStorage,window) - crashes SSR
Gotchas & Edge Cases:
storeToRefs()works for state and getters only, not actions - destructure actions directly- Options stores have built-in
$reset(), Setup stores do not - implement manually or use a plugin - Store instances are singletons per Pinia instance - in SSR, each request needs its own Pinia
$patchwith a function allows accessing current state:store.$patch(state => { state.items.push(item) })- Arrow functions in Options store getters have no
this- usefunctionkeyword or state parameter
For complete anti-pattern examples, see reference.md.
</red_flags>
Detailed Resources:
- examples/core.md - Options store, Setup store, storeToRefs, composing stores
- examples/persistence.md - Selective persistence with pinia-plugin-persistedstate
- examples/testing.md - Unit testing stores, component testing with createTestingPinia
- examples/plugins.md - Logger plugin, reset plugin for Setup stores
- examples/ssr.md - SSR-safe stores, hydration, client-only guards
- reference.md - Decision frameworks, anti-patterns, TypeScript patterns, performance tips
<critical_reminders>
CRITICAL REMINDERS
(You MUST use a data fetching solution for ALL server/API data - NEVER put API responses in Pinia stores)
(You MUST use storeToRefs() when destructuring state from stores - direct destructuring loses reactivity)
(You MUST return ALL state properties in Setup stores - private state breaks SSR and DevTools)
(You MUST use named exports ONLY - NO default exports in any store files)
(You MUST use named constants for ALL numbers - NO magic numbers in state code)
Failure to follow these rules will cause reactivity bugs, SSR hydration errors, and DevTools failures.
</critical_reminders>
More from agents-inc/skills
web-animation-css-animations
CSS Animation patterns - transitions, keyframes, scroll-driven animations, @property, GPU-accelerated properties, accessibility with prefers-reduced-motion
21web-testing-playwright-e2e
Playwright E2E testing patterns - test structure, Page Object Model, locator strategies, assertions, network mocking, visual regression, parallel execution, fixtures, and configuration
19web-animation-view-transitions
View Transitions API patterns - same-document transitions, cross-document MPA transitions, shared element animations, pseudo-element styling, accessibility
18web-animation-framer-motion
Motion (formerly Framer Motion) animation patterns - motion components, variants, gestures, layout animations, scroll-linked animations, accessibility
18web-styling-cva
Class Variance Authority - type-safe component variant styling with cva(), compound variants, and VariantProps
17web-i18n-next-intl
Type-safe i18n for Next.js App Router
17