Pinia v3 Vue state management with defineStore, getters, actions. Use for Vue 3 stores, Nuxt SSR, Vuex migration, or encountering store composition, hydration, testing errors.
SKILL.md
Pinia v3 - Vue State Management
Status: Production Ready ✅
Last Updated: 2025-11-11
Dependencies: Vue 3 (or Vue 2.7 with @vue/composition-api)
Latest Versions: pinia@^3.0.4, @pinia/nuxt@^0.11.2, @pinia/testing@^1.0.2
Quick Start (5 Minutes)
1. Install Pinia
bun add pinia
# or
bun add pinia
# or
bun add pinia
For Vue <2.7 users: Also install @vue/composition-api with bun add @vue/composition-api
Why this matters:
Pinia is the official Vue state management library
Provides better TypeScript support than Vuex
Eliminates mutations and namespacing complexity
Full DevTools support with time-travel debugging
2. Create and Register Pinia Instance
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
CRITICAL:
Install Pinia BEFORE using any store
Call app.use(pinia) before mounting the app
Only one Pinia instance per application (unless SSR)
✅ Define all state properties in state() or return them from setup stores
✅ Use storeToRefs() when destructuring state/getters in components
✅ Call app.use(pinia) BEFORE mounting the app
✅ Return all state from setup stores (private state breaks SSR/DevTools)
✅ Call useStore() inside functions/callbacks when used outside components
✅ Use acceptHMRUpdate() for development HMR support
✅ Type return values when getters use this to access other getters
✅ Use devalue for SSR state serialization (prevents XSS)
✅ Hydrate state BEFORE calling any useStore() on the client (SSR)
✅ Call all useStore() BEFORE any await in async actions (SSR)
Never Do
❌ Add state properties dynamically after store creation
❌ Destructure store directly without storeToRefs() (loses reactivity)
❌ Use arrow functions for actions (need this context)
❌ Return private state in setup stores (breaks SSR/DevTools/plugins)
❌ Call useStore() at module top-level (before Pinia installed)
❌ Create circular dependencies between stores (both reading each other's state)
❌ Use JSON.stringify() for SSR serialization (vulnerable to XSS)
❌ Call useStore() after await in actions (breaks SSR)
❌ Forget to type getter return values when using this
❌ Skip beforeEach(() => setActivePinia(createPinia())) in unit tests
Known Issues Prevention
This skill prevents 12 documented issues:
Issue #1: Lost Reactivity from Direct Destructuring
Error: State changes don't update in template after destructuring
Why It Happens: JavaScript destructuring breaks Vue reactivity
Prevention: Always use storeToRefs() for state/getters
Issue #2: Cannot Add State Properties Dynamically
Error: New properties added after store creation aren't reactive
Why It Happens: Pinia needs all properties defined upfront for reactivity
Prevention: Declare all properties in state(), even if initially undefined
Issue #3: Store Not Found Before Pinia Install
Error: getActivePinia() returns undefined
Why It Happens: Calling useStore() before app.use(pinia)Prevention: Call app.use(pinia) before mounting or accessing stores
Issue #4: Setup Store Private State Breaks SSR
Error: State not serialized/hydrated correctly in SSR
Why It Happens: Properties not returned from setup aren't tracked
Prevention: Return ALL state properties from setup stores
Issue #5: Getters with this Don't Infer Types
Error: TypeScript can't infer return type when getter uses thisSource: Known TypeScript limitation with Pinia
Prevention: Explicitly type return value: getterName(): ReturnType { ... }
Issue #6: Options API Store Suffix Confusion
Error: Can't find this.counterStore in component
Why It Happens: mapStores() automatically adds 'Store' suffix
Prevention: Use store name + 'Store' or call setMapStoreSuffix()
Issue #7: Actions Called After await Break SSR
Error: Wrong Pinia instance used in SSR, causing state pollution
Why It Happens: await changes execution context in async functions
Prevention: Call all useStore() before any await statements
Issue #8: Circular Store Dependencies Crash App
Error: Maximum call stack exceeded
Why It Happens: Both stores read each other's state during initialization
Prevention: Use getters/actions for cross-store access, not setup-time reads
Issue #9: XSS Vulnerability in SSR State Serialization
Error: User input in state can execute malicious scripts
Why It Happens: JSON.stringify() doesn't escape executable code
Prevention: Use devalue library for safe serialization
Issue #10: HMR Doesn't Work in Development
Error: Changes to store require full page reload
Why It Happens: Vite/webpack HMR not configured for store
Prevention: Add acceptHMRUpdate() block to each store file
Error: Store state contains non-serializable functions
Why It Happens: Option stores state() can only return writable refs
Prevention: Use setup stores for complex composables, or extract only writable state
Issue #12: State Not Reset Between Unit Tests
Error: Tests affect each other, sporadic failures
Why It Happens: Single Pinia instance shared across tests
Prevention: beforeEach(() => setActivePinia(createPinia())) in test suites
Package Versions (Verified 2026-08-03)
Core:pinia@^3.0.4, vue@^3.5.24Nuxt:@pinia/nuxt@^0.11.2, nuxt@^4.0.0 (Nuxt 3 reached EOL on 2026-07-31; v4 is the current stable line)
Testing:@pinia/testing@^1.0.2, vitest@^2.0.0SSR:devalue@^5.3.2 (for safe serialization)
Common Patterns
See reference files for complete pattern examples: