Learn-skills.dev web-state-pinia

Pinia stores, Vue 3 state patterns. Use when managing client state in Vue applications, choosing between Options/Setup stores, composing stores, or implementing persistence.

install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/agents-inc/skills/web-state-pinia" ~/.claude/skills/neversight-learn-skills-dev-web-state-pinia && rm -rf "$T"
manifest: data/skills-md/agents-inc/skills/web-state-pinia/SKILL.md
source content

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>

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 TypeSolutionReason
Server/API dataData fetching solutionCaching, synchronization, loading states
Shared client statePiniaReactivity, DevTools, persistence
Component-local state
ref()
/
reactive()
Simpler, no overhead
URL state (filters)Route query paramsShareable, bookmarkable
</philosophy>
<patterns>

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.

</patterns>

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