Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
secondsky avatar

Pinia V3

  • 137 installs
  • 202 repo stars
  • Updated August 4, 2026
  • secondsky/claude-skills

Helps with ai & agent building tasks during AI-assisted development.

About

pinia-v3 is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.

  • pinia-v3
  • AI & Agent Building
  • AI-coding skill

Pinia V3 by the numbers

  • 137 all-time installs (skills.sh)
  • +10 installs in the week ending Jul 27, 2026 (Skillselion tracking)
  • Ranked #3,529 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/secondsky/claude-skills --skill pinia-v3

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs137
repo stars202
Last updatedAugust 4, 2026
Repositorysecondsky/claude-skills

What it does

Helps with ai & agent building tasks during AI-assisted development.

Files

SKILL.mdMarkdownGitHub ↗

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

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() (not JSON.stringify)
  • Client: Hydrate BEFORE calling useStore()
  • Critical: Call all useStore() BEFORE await in 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: 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

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 storesreferences/state-getters-actions.md
  • Persistence pluginsreferences/plugins-composables.md
  • Form storesreferences/store-syntax-guide.md (setup store examples)
  • Router integrationreferences/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: 1. Ensure app.use(pinia) is called before mounting 2. If outside component, call useStore() inside callback/function 3. 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 pinia package
  • [ ] 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?

1. Check official docs: https://pinia.vuejs.org/ 2. Review "Known Issues Prevention" section above 3. Verify setup checklist is complete 4. Check for TypeScript configuration issues 5. Ensure Pinia is installed before using stores

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.