Unnamed Skill
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.
$ Installieren
git clone https://github.com/secondsky/claude-skills /tmp/claude-skills && cp -r /tmp/claude-skills/plugins/pinia-v3/skills/pinia-v3 ~/.claude/skills/claude-skills// tip: Run this command in your terminal to install the skill
name: pinia-v3 description: 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.
Keywords: pinia, vue state management, pinia stores, defineStore, vue 3 state, state management, getters, actions, pinia plugins, pinia ssr, nuxt pinia, vuex migration, store composition, pinia testing, setup stores, option stores, storeToRefs, mapState, mapActions, state hydration, pinia nuxt module, createPinia, useStore, pinia devtools, pinia hmr, hot module replacement license: MIT
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)
3. Define Your First Store
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Eduardo'
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
4. Use Store in Components
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double: {{ counter.doubleCount }}</p>
<button @click="counter.increment">Increment</button>
</div>
</template>
The Two Store Syntaxes
Load references/store-syntax-guide.md for complete comparison of Option vs Setup stores.
Quick Overview
Pinia supports two store definition syntaxes:
Option Stores:
- Similar to Vue Options API
- Built-in
$reset()method - Best for: Simpler use cases, teams familiar with Vuex
Setup Stores:
- Uses Composition API pattern
- Full composables integration
- Best for: Advanced patterns, need watchers/VueUse integration
â Load references/store-syntax-guide.md for: Complete syntax comparison, examples, choosing criteria
State, Getters, and Actions
Load references/state-getters-actions.md for complete API reference.
Quick Reference
State:
- Define in
state: () => ({...})(option) orref()(setup) - Access directly:
store.count - Mutate directly:
store.count++orstore.$patch({...}) - Reset:
store.$reset()(option stores only)
Getters:
- Computed properties:
getters: { double: (state) => state.count * 2 } - Access other getters with
this(must type return value)
Actions:
- Business logic:
actions: { increment() { this.count++ } } - Can be async
- Access other stores directly
Store Destructuring:
import { storeToRefs } from 'pinia'
// â
For reactivity
const { name, count } = storeToRefs(store)
// â
Actions can destructure directly
const { increment } = store
â Load references/state-getters-actions.md for: Complete API, subscriptions, store composition patterns, Options API usage
Plugins and Composables
Load references/plugins-composables.md for complete plugin and composables guide.
Plugin Basics
pinia.use(({ store, options }) => {
// Add properties to every store
return { customProperty: 'value' }
})
Composables Integration
Option Stores: Limited to useLocalStorage style in state()
Setup Stores: Full VueUse/composables support
â Load references/plugins-composables.md for: Complete plugin patterns, VueUse integration, TypeScript typing, common patterns (persistence, router, logger)
Using Stores Outside Components
The Problem
Stores need the Pinia instance, which is auto-injected in components but not available in module scope.
â Wrong: Accessing Store at Module Level
// router.ts
import { useUserStore } from '@/stores/user'
// â Fails: Pinia not installed yet
const userStore = useUserStore()
router.beforeEach((to) => {
if (userStore.isLoggedIn) { /* ... */ }
})
â Right: Accessing Store Inside Callbacks
// router.ts
import { useUserStore } from '@/stores/user'
router.beforeEach((to) => {
// â
Works: Called after Pinia is installed
const userStore = useUserStore()
if (userStore.isLoggedIn) { /* ... */ }
})
Why it works: Router guards execute AFTER app.use(pinia) completes.
SSR: Explicit Pinia Instance
// server-side
export function setupRouter(pinia) {
router.beforeEach((to) => {
const userStore = useUserStore(pinia) // Pass explicitly
})
}
Server-Side Rendering & Nuxt
Load references/ssr-and-nuxt.md for complete SSR and Nuxt integration guide.
SSR Quick Reference
State Hydration:
- Server: Serialize with
devalue()(notJSON.stringify) - Client: Hydrate BEFORE calling
useStore() - Critical: Call all
useStore()BEFOREawaitin actions
Nuxt 3/4 Integration
bunx nuxi@latest module add pinia
Auto-imports: defineStore, storeToRefs, usePinia, acceptHMRUpdate, all stores
â Load references/ssr-and-nuxt.md for: Complete SSR patterns, Nuxt configuration, server-side data fetching, SSR pitfalls, debugging
Testing
Load references/testing-guide.md for complete testing guide.
Testing Quick Start
import { setActivePinia, createPinia } from 'pinia'
beforeEach(() => {
setActivePinia(createPinia()) // Fresh Pinia for each test
})
Component Testing
bun add -d @pinia/testing
import { createTestingPinia } from '@pinia/testing'
mount(Component, {
global: { plugins: [createTestingPinia()] }
})
â Load references/testing-guide.md for: Complete test patterns, stubbing actions, mocking getters, async testing, SSR testing
Hot Module Replacement (HMR)
Vite Setup
// stores/counter.ts
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useCounterStore = defineStore('counter', {
// store definition
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot))
}
Webpack Setup
if (import.meta.webpackHot) {
import.meta.webpackHot.accept(acceptHMRUpdate(useCounterStore, import.meta.webpackHot))
}
Benefits:
- Edit stores without full page reload
- Preserve application state during development
- Faster development iteration
Options API Usage
For projects still using Options API, load complete mapper documentation.
â Load references/state-getters-actions.md for: Complete Options API integration, all mappers (mapStores, mapState, mapWritableState, mapActions)
Migrating from Vuex
Load references/vuex-migration.md for complete migration guide.
Quick Conversion Overview
Key Changes:
- Remove
namespaced(automatic via store ID) - Eliminate
mutations(direct state mutation) - Replace
commit()with direct mutations - Replace
rootState/rootGetterswith store imports - Use
store.$reset()instead of custom clear mutations
Directory: store/modules/ â stores/ (each module = separate store)
â Load references/vuex-migration.md for: Complete conversion steps, component migration, checklist, gradual migration strategy
Critical Rules
Always Do
â
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 this
Source: 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
Issue #11: Composables Return Functions Break Option Stores
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 2025-11-21)
Core: pinia@^3.0.4, vue@^3.5.24
Nuxt: @pinia/nuxt@^0.11.2, nuxt@^3.13.0
Testing: @pinia/testing@^1.0.2, vitest@^1.0.0
SSR: devalue@^5.3.2 (for safe serialization)
Common Patterns
See reference files for complete pattern examples:
- Authentication stores â
references/state-getters-actions.md - Persistence plugins â
references/plugins-composables.md - Form stores â
references/store-syntax-guide.md(setup store examples) - Router integration â
references/state-getters-actions.md(accessing stores outside components)
Official Documentation
- Pinia: https://pinia.vuejs.org/
- Getting Started: https://pinia.vuejs.org/getting-started.html
- Core Concepts: https://pinia.vuejs.org/core-concepts/
- SSR Guide: https://pinia.vuejs.org/ssr/
- Nuxt Integration: https://pinia.vuejs.org/ssr/nuxt.html
- Testing: https://pinia.vuejs.org/cookbook/testing.html
- Vuex Migration: https://pinia.vuejs.org/cookbook/migration-vuex.html
- GitHub: https://github.com/vuejs/pinia
Troubleshooting
Problem: "getActivePinia() was called with no active Pinia"
Solution:
- Ensure
app.use(pinia)is called before mounting - If outside component, call
useStore()inside callback/function - For SSR, pass pinia instance explicitly:
useStore(pinia)
Problem: State changes don't update in template
Solution: Use storeToRefs() instead of direct destructuring
Problem: Getter using this has TypeScript errors
Solution: Explicitly type the return value: myGetter(): ReturnType { return this.otherGetter }
Problem: $reset() not available in setup store
Solution: Implement custom reset manually:
function $reset() {
count.value = 0
name.value = ''
}
return { count, name, $reset }
Problem: HMR not working for stores
Solution: Add HMR acceptance block:
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useMyStore, import.meta.hot))
}
Problem: Tests fail intermittently
Solution: Create fresh Pinia in beforeEach():
beforeEach(() => {
setActivePinia(createPinia())
})
When to Load References
Load references/store-syntax-guide.md when:
- Need detailed comparison between Option and Setup store syntaxes
- Deciding which syntax to use for a new store
- Questions about Option vs Setup stores trade-offs
- Need complete examples of both syntaxes
Load references/state-getters-actions.md when:
- Need complete API reference for state, getters, or actions
- Questions about
$patch,$subscribe, or$onAction - Implementing store composition patterns
- Using Options API mappers (
mapStores,mapState,mapActions) - Accessing stores outside components (router, plugins)
Load references/plugins-composables.md when:
- Creating custom Pinia plugins
- Integrating VueUse or other composables into stores
- Need persistence, routing, or logging plugin patterns
- Questions about TypeScript typing for plugins
- Advanced composables integration
Load references/ssr-and-nuxt.md when:
- Setting up server-side rendering
- Integrating with Nuxt 3/4
- Questions about state hydration or serialization
- SSR-related errors (wrong Pinia instance, hydration mismatch)
- Nuxt auto-imports or configuration
- Server-side data fetching patterns
Load references/testing-guide.md when:
- Setting up unit tests for stores
- Testing components that use Pinia stores
- Need to stub actions or mock getters
- Questions about
createTestingPinia - Testing SSR stores
- Vitest or testing framework integration
Load references/vuex-migration.md when:
- Migrating existing Vuex codebase to Pinia
- Questions about VuexâPinia conversion
- Need migration checklist or examples
- Gradual migration strategy needed
Complete Setup Checklist
- Installed
piniapackage - Created Pinia instance with
createPinia() - Registered with
app.use(pinia)before mounting - Created stores directory (e.g.,
src/stores/) - Defined at least one store with
defineStore() - Used
storeToRefs()when destructuring in components - Typed getter return values when using
this - Added HMR support with
acceptHMRUpdate()(development) - Configured SSR hydration (if using SSR)
- Configured
@pinia/nuxt(if using Nuxt) - Set up testing with
createTestingPinia()(if testing) - All stores follow consistent naming:
use[Name]Store - Verified DevTools integration works
Questions? Issues?
- Check official docs: https://pinia.vuejs.org/
- Review "Known Issues Prevention" section above
- Verify setup checklist is complete
- Check for TypeScript configuration issues
- Ensure Pinia is installed before using stores
Repository
