All skills
onmax avatar

/pinia

@de09c7b official
by Maxonmax/nuxt-skills715 stars
38

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/onmax/nuxt-skills/pinia

This session only. Nothing lands on disk.

referencescore-stores.md

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

Pinia Stores

Stores are defined using defineStore() with a unique name. Each store has three core concepts: state, getters, and actions.

Defining Stores

Option Stores

Similar to Vue's Options API:

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Eduardo',
  }),
  getters: {
    doubleCount: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

Think of state as data, getters as computed, and actions as methods.

Setup Stores (Recommended)

Uses Composition API syntax - more flexible and powerful:

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const name = ref('Eduardo')
  const doubleCount = computed(() => count.value * 2)

  function increment() {
    count.value++
  }

  return { count, name, doubleCount, increment }
})

In Setup Stores: ref() → state, computed() → getters, function() → actions.

Important: You must return all state properties for Pinia to track them.

Using Stores

<script setup>
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()
// Access: store.count, store.doubleCount, store.increment()
</script>

Destructuring with storeToRefs

<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()

// ❌ Breaks reactivity
const { name, doubleCount } = store

// ✅ Preserves reactivity for state/getters
const { name, doubleCount } = storeToRefs(store)

// ✅ Actions can be destructured directly
const { increment } = store
</script>

State

State is defined as a function returning the initial state.

TypeScript

Type inference works automatically. For complex types:

interface UserInfo {
  name: string
  age: number
}

export const useUserStore = defineStore('user', {
  state: () => ({
    userList: [] as UserInfo[],
    user: null as UserInfo | null,
  }),
})

Or use an interface for the return type:

interface State {
  userList: UserInfo[]
  user: UserInfo | null
}

export const useUserStore = defineStore('user', {
  state: (): State => ({
    userList: [],
    user: null,
  }),
})

Accessing and Modifying

const store = useStore()
store.count++
<input v-model="store.count" type="number" />

Mutating with $patch

Apply multiple changes at once:

// Object syntax
store.$patch({
  count: store.count + 1,
  name: 'DIO',
})

// Function syntax (for complex mutations)
store.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

Resetting State

Option Stores have built-in $reset(). For Setup Stores, implement your own:

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)

  function $reset() {
    count.value = 0
  }

  return { count, $reset }
})

Subscribing to State Changes

cartStore.$subscribe((mutation, state) => {
  mutation.type // 'direct' | 'patch object' | 'patch function'
  mutation.storeId // 'cart'
  mutation.payload // patch object (only for 'patch object')

  localStorage.setItem('cart', JSON.stringify(state))
})

// Options
cartStore.$subscribe(callback, { flush: 'sync' }) // Immediate
cartStore.$subscribe(callback, { detached: true }) // Keep after unmount

Getters

Getters are computed values, equivalent to Vue's computed().

Basic Getters

getters: {
  doubleCount: (state) => state.count * 2,
}

Accessing Other Getters

Use this with explicit return type:

getters: {
  doubleCount: (state) => state.count * 2,
  doublePlusOne(): number {
    return this.doubleCount + 1
  },
},

Getters with Arguments

Return a function (note: loses caching):

getters: {
  getUserById: (state) => {
    return (userId: string) => state.users.find((user) => user.id === userId)
  },
},

Cache within parameterized getters:

getters: {
  getActiveUserById(state) {
    const activeUsers = state.users.filter((user) => user.active)
    return (userId: string) => activeUsers.find((user) => user.id === userId)
  },
},

Accessing Other Stores in Getters

import { useOtherStore } from './other-store'

getters: {
  combined(state) {
    const otherStore = useOtherStore()
    return state.localData + otherStore.data
  },
},

Actions

Actions are methods for business logic. Unlike getters, they can be asynchronous.

Defining Actions

actions: {
  increment() {
    this.count++
  },
  randomizeCounter() {
    this.count = Math.round(100 * Math.random())
  },
},

Async Actions

actions: {
  async registerUser(login: string, password: string) {
    try {
      this.userData = await api.post({ login, password })
    } catch (error) {
      return error
    }
  },
},

Accessing Other Stores in Actions

import { useAuthStore } from './auth-store'

actions: {
  async fetchUserPreferences() {
    const auth = useAuthStore()
    if (auth.isAuthenticated) {
      this.preferences = await fetchPreferences()
    }
  },
},

SSR: Call all useStore() before any await:

async orderCart() {
  // ✅ Call stores before await
  const user = useUserStore()

  await apiOrderCart(user.token, this.items)
  // ❌ Don't call useStore() after await in SSR
}

Subscribing to Actions

const unsubscribe = someStore.$onAction(
  ({ name, store, args, after, onError }) => {
    const startTime = Date.now()
    console.log(`Start "${name}" with params [${args.join(', ')}]`)

    after((result) => {
      console.log(`Finished "${name}" after ${Date.now() - startTime}ms`)
    })

    onError((error) => {
      console.warn(`Failed "${name}": ${error}`)
    })
  }
)

unsubscribe() // Cleanup

Keep subscription after component unmount:

someStore.$onAction(callback, true)

Options API Helpers

import { mapState, mapWritableState, mapActions } from 'pinia'
import { useCounterStore } from '../stores/counter'

export default {
  computed: {
    // Readonly state/getters
    ...mapState(useCounterStore, ['count', 'doubleCount']),
    // Writable state
    ...mapWritableState(useCounterStore, ['count']),
  },
  methods: {
    ...mapActions(useCounterStore, ['increment']),
  },
}

Accessing Global Providers in Setup Stores

import { inject } from 'vue'
import { useRoute } from 'vue-router'
import { defineStore } from 'pinia'

export const useSearchFilters = defineStore('search-filters', () => {
  const route = useRoute()
  const appProvided = inject('appProvided')

  // Don't return these - access them directly in components
  return { /* ... */ }
})
<!-- Source references: - https://pinia.vuejs.org/core-concepts/ - https://pinia.vuejs.org/core-concepts/state.html - https://pinia.vuejs.org/core-concepts/getters.html - https://pinia.vuejs.org/core-concepts/actions.html -->

Source: SKILL.md on GitHub

No alerts9d3 checks · Risk SAFE
  • Gen Agent Trust Hub9d

    The skill provides comprehensive documentation and best practices for the Pinia state management library. It covers defining stores, using plugins, SSR hydration, and unit testing. No malicious patterns or security risks were identified; the skill includes security-conscious recommendations like using 'devalue' for XSS-safe serialization during server-side rendering.

  • Socket9d

    No alerts

  • Snyk9d

    Risk: LOW · No issues

Signed by skilld at de09c7b. 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 last week
Other metadata
metadata
{
  "author": "Anthony Fu",
  "version": "2026.1.28",
  "source": "Generated from https://github.com/vuejs/pinia, scripts located at https://github.com/antfu/skills"
}

README badge

README badge for onmax/nuxt-skills/pinia