--- 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); }); ```