web-state-pinia

Installation
SKILL.md

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() or reactive())
  • 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:

  1. Modular by Default - Each store is independent, no nested modules
  2. No Mutations - Actions handle all state changes directly
  3. Full TypeScript Support - Type inference works out of the box
  4. 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

  1. Never destructure state directly - loses reactivity
  2. Use storeToRefs() for state/getters - preserves reactivity
  3. 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-persistedstate for most cases
  • Only persist user preferences and non-sensitive data
  • Use pick option 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

  1. Unit tests: Test store actions/getters directly with setActivePinia()
  2. Component tests: Use createTestingPinia() for component mounting
  3. 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
  • $patch with a function allows accessing current state: store.$patch(state => { state.items.push(item) })
  • Arrow functions in Options store getters have no this - use function keyword or state parameter

For complete anti-pattern examples, see reference.md.

</red_flags>


Detailed Resources:


<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>

Related skills
Installs
2
GitHub Stars
6
First Seen
Apr 7, 2026