All skills
antfu avatar

/pinia

@d02c484 official
by Anthony Fuantfu/skills5.9k stars
335

Pinia official Vue state management library, type-safe and extensible. Use when defining stores, working with state/getters/actions, or implementing store patterns in Vue apps.

Use this Skill: https://skilld.dev/gh/antfu/skills/pinia

This session only. Nothing lands on disk.

referencesbest-practices-testing.md

≈1.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Testing Stores

Unit Testing Stores

Create a fresh pinia instance for each test:

import { setActivePinia, createPinia } from 'pinia'
import { useCounterStore } from '../src/stores/counter'

describe('Counter Store', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('increments', () => {
    const counter = useCounterStore()
    expect(counter.n).toBe(0)
    counter.increment()
    expect(counter.n).toBe(1)
  })
})

With Plugins

import { setActivePinia, createPinia } from 'pinia'
import { createApp } from 'vue'
import { somePlugin } from '../src/stores/plugin'

const app = createApp({})

beforeEach(() => {
  const pinia = createPinia().use(somePlugin)
  app.use(pinia)
  setActivePinia(pinia)
})

Testing Components

Install @pinia/testing:

npm i -D @pinia/testing

Use createTestingPinia():

import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'
import { useSomeStore } from '@/stores/myStore'

const wrapper = mount(Counter, {
  global: {
    plugins: [createTestingPinia()],
  },
})

const store = useSomeStore()

// Manipulate state directly
store.name = 'new name'
store.$patch({ name: 'new name' })

// Actions are stubbed by default
store.someAction()
expect(store.someAction).toHaveBeenCalledTimes(1)

Initial State

Set initial state for tests:

const wrapper = mount(Counter, {
  global: {
    plugins: [
      createTestingPinia({
        initialState: {
          counter: { n: 20 }, // Store name → initial state
        },
      }),
    ],
  },
})

Action Stubbing

Stubbing Limitations in Setup Stores

stubActions only replaces actions on the store instance. In a Setup store, an action that calls another action via its closed-over function reference bypasses the stub:

export const useCounterStore = defineStore('counter', () => {
  function increment() { /* ... */ }

  function incrementTwice() {
    increment() // ❌ closed-over reference — stub never used
    increment()
  }

  return { increment, incrementTwice }
})

Stubbing affects store.increment() but not the internal calls in incrementTwice(). Options stores don't have this issue — internal this.increment() calls go through the store instance and hit the stub.

To make an internal call stubbable in a Setup store, route it through the store (overkill just for a test — prefer testing the real implementation instead):

function incrementTwice() {
  const store = useCounterStore()
  store.increment() // ✅ goes through the store, uses the stub
  store.increment()
}

Execute Real Actions

createTestingPinia({ stubActions: false })

Selective Stubbing

// Only stub specific actions
createTestingPinia({
  stubActions: ['increment', 'reset'],
})

// Or use a function
createTestingPinia({
  stubActions: (actionName, store) => {
    if (actionName.startsWith('set')) return true
    return false
  },
})

Mock Action Return Values

import type { Mock } from 'vitest'

// After getting store
store.someAction.mockResolvedValue('mocked value')

Mocking Getters

Getters are writable in tests:

const pinia = createTestingPinia()
const counter = useCounterStore(pinia)

counter.double = 3 // Override computed value

// Reset to default behavior
counter.double = undefined
counter.double // Now computed normally

Custom Spy Function

If not using Jest/Vitest with globals:

import { vi } from 'vitest'

createTestingPinia({
  createSpy: vi.fn,
})

With Sinon:

import sinon from 'sinon'

createTestingPinia({
  createSpy: sinon.spy,
})

Pinia Plugins in Tests

Pass plugins to createTestingPinia():

import { somePlugin } from '../src/stores/plugin'

createTestingPinia({
  stubActions: false,
  plugins: [somePlugin],
})

Don't use testingPinia.use(MyPlugin) - pass plugins in options.

Type-Safe Mocked Store

import type { Mock } from 'vitest'
import type { Store, StoreDefinition } from 'pinia'

function mockedStore<TStoreDef extends () => unknown>(
  useStore: TStoreDef
): TStoreDef extends StoreDefinition<infer Id, infer State, infer Getters, infer Actions>
  ? Store<Id, State, Record<string, never>, {
      [K in keyof Actions]: Actions[K] extends (...args: any[]) => any
        ? Mock<Actions[K]>
        : Actions[K]
    }>
  : ReturnType<TStoreDef> {
  return useStore() as any
}

// Usage
const store = mockedStore(useSomeStore)
store.someAction.mockResolvedValue('value') // Typed!

E2E Tests

No special handling needed - Pinia works normally.

<!-- Source references: - https://pinia.vuejs.org/cookbook/testing.html -->

Source: SKILL.md on GitHub

No alerts3d5 checks · Risk SAFE
  • Gen Agent Trust Hub3d

    This skill is a safe and well-structured documentation reference for Pinia, the official Vue state management library. It provides accurate code snippets, follows security best practices (such as recommending XSS protection for SSR), and utilizes official packages from the Vue and Nuxt ecosystems.

  • Socket3d

    No alerts

  • Snyk3d

    Risk: LOW · No issues

  • Runlayer7mo

    1/11 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at d02c484. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 4 days ago
Other metadata
metadata
{
  "author": "Anthony Fu",
  "version": "2026.9.25",
  "source": "Generated from https://github.com/vuejs/pinia, scripts located at https://github.com/antfu/skills"
}

README badge

README badge for antfu/skills/pinia

Pinia is the official Vue state management library providing type-safe, extensible stores with support for both Options and Composition APIs. Use it for defining reactive state, getters, and actions in Vue applications, with built-in TypeScript support and devtools integration.

Generated from the current SKILL.md.

Does Pinia work with both Options API and Composition API?
Yes. Pinia supports both Options API and Composition API styles with first-class TypeScript support for both.
How do I preserve reactivity when destructuring state from a store?
Use `storeToRefs()` when destructuring state and getters to maintain reactivity. Actions can be destructured directly since they're bound to the store.
Can I use Pinia outside of Vue components?
Yes. Pinia stores work in navigation guards, plugins, middlewares, and other contexts outside components, but you should call stores inside functions rather than at module scope, especially for SSR.
How do I test components that use Pinia stores?
Use `@pinia/testing` for unit testing with built-in support for mocking and stubbing stores.
Does Pinia have Nuxt integration?
Yes. Pinia integrates with Nuxt with auto-imports, auto-setup, and SSR-specific best practices documented in the skill.

Generated from the current SKILL.md. These answers refresh after source changes.