--- name: plugins description: Extend stores with custom properties, methods, and behavior --- # Plugins Plugins extend all stores with custom properties, methods, or behavior. ## Basic Plugin ```ts import { createPinia } from 'pinia' function SecretPiniaPlugin() { return { secret: 'the cake is a lie' } } const pinia = createPinia() pinia.use(SecretPiniaPlugin) // In any store const store = useStore() store.secret // 'the cake is a lie' ``` ## Plugin Context Plugins receive a context object: ```ts import { PiniaPluginContext } from 'pinia' export function myPiniaPlugin(context: PiniaPluginContext) { context.pinia // pinia instance context.app // Vue app instance context.store // store being augmented context.options // store definition options } ``` ## Adding Properties Return an object to add properties (tracked in devtools): ```ts pinia.use(() => ({ hello: 'world' })) ``` Or set directly on store: ```ts pinia.use(({ store }) => { store.hello = 'world' // For devtools visibility in dev mode if (process.env.NODE_ENV === 'development') { store._customProperties.add('hello') } }) ``` ## Adding State Add to both `store` and `store.$state` for SSR/devtools: ```ts import { toRef, ref } from 'vue' pinia.use(({ store }) => { if (!store.$state.hasOwnProperty('hasError')) { const hasError = ref(false) store.$state.hasError = hasError } store.hasError = toRef(store.$state, 'hasError') }) ``` ## Adding External Properties Wrap non-reactive objects with `markRaw()`: ```ts import { markRaw } from 'vue' import { router } from './router' pinia.use(({ store }) => { store.router = markRaw(router) }) ``` ## Custom Store Options Define custom options consumed by plugins: ```ts // Store definition defineStore('search', { actions: { searchContacts() { /* ... */ }, }, debounce: { searchContacts: 300, }, }) // Plugin reads custom option import debounce from 'lodash/debounce' pinia.use(({ options, store }) => { if (options.debounce) { return Object.keys(options.debounce).reduce((acc, action) => { acc[action] = debounce(store[action], options.debounce[action]) return acc }, {}) } }) ``` For Setup Stores, pass options as third argument: ```ts defineStore( 'search', () => { /* ... */ }, { debounce: { searchContacts: 300 }, } ) ``` ## TypeScript Augmentation ### Custom Properties ```ts import 'pinia' import type { Router } from 'vue-router' declare module 'pinia' { export interface PiniaCustomProperties { router: Router hello: string } } ``` ### Custom State ```ts declare module 'pinia' { export interface PiniaCustomStateProperties { hasError: boolean } } ``` ### Custom Options ```ts declare module 'pinia' { export interface DefineStoreOptionsBase { debounce?: Partial, number>> } } ``` ## Subscribe in Plugins ```ts pinia.use(({ store }) => { store.$subscribe(() => { // React to state changes }) store.$onAction(() => { // React to actions }) }) ``` ## Nuxt Plugin Create a Nuxt plugin to add Pinia plugins: ```ts // plugins/myPiniaPlugin.ts import { PiniaPluginContext } from 'pinia' function MyPiniaPlugin({ store }: PiniaPluginContext) { store.$subscribe((mutation) => { console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`) }) return { creationTime: new Date() } } export default defineNuxtPlugin(({ $pinia }) => { $pinia.use(MyPiniaPlugin) }) ```