secondsky/claude-skills/pinia-v3

Trust tier 2
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

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.

Install

npx vskill@latest install secondsky/claude-skills/pinia-v3

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
bash
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
typescript
// 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
typescript
// 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
vue
<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) or ref() (setup)
  • Access directly: store.count
  • Mutate directly: store.count++ or store.$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:

typescript
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
typescript
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
typescript
// 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
typescript
// 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
typescript
// 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() (not JSON.stringify)
  • Client: Hydrate BEFORE calling useStore()
  • Critical: Call all useStore() BEFORE await in actions
Nuxt 3/4 Integration
bash
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
typescript
import { setActivePinia, createPinia } from 'pinia'
beforeEach(() => {
setActivePinia(createPinia()) // Fresh Pinia for each test
})
Component Testing
bash
bun add -d @pinia/testing
typescript
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
typescript
// 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
typescript
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:

  1. Remove namespaced (automatic via store ID)
  2. Eliminate mutations (direct state mutation)
  3. Replace commit() with direct mutations
  4. Replace rootState/rootGetters with store imports
  5. 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

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 93 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
216
Repository forks
31
7-day trend score
27

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 23 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/secondsky/claude-skills/pinia-v3/badge)](https://verified-skill.com/skills/secondsky/claude-skills/pinia-v3)

Something wrong with this skill or its page? Tell us.