Files
stack/packages/mosaic/framework/skills/pinia/references/features-plugins.md
T
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

3.6 KiB

name, description
name description
plugins Extend stores with custom properties, methods, and behavior

Plugins

Plugins extend all stores with custom properties, methods, or behavior.

Basic Plugin

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:

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

pinia.use(() => ({ hello: 'world' }));

Or set directly on store:

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:

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

import { markRaw } from 'vue';
import { router } from './router';

pinia.use(({ store }) => {
  store.router = markRaw(router);
});

Custom Store Options

Define custom options consumed by plugins:

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

defineStore(
  'search',
  () => {
    /* ... */
  },
  {
    debounce: { searchContacts: 300 },
  },
);

TypeScript Augmentation

Custom Properties

import 'pinia';
import type { Router } from 'vue-router';

declare module 'pinia' {
  export interface PiniaCustomProperties {
    router: Router;
    hello: string;
  }
}

Custom State

declare module 'pinia' {
  export interface PiniaCustomStateProperties<S> {
    hasError: boolean;
  }
}

Custom Options

declare module 'pinia' {
  export interface DefineStoreOptionsBase<S, Store> {
    debounce?: Partial<Record<keyof StoreActions<Store>, number>>;
  }
}

Subscribe in Plugins

pinia.use(({ store }) => {
  store.$subscribe(() => {
    // React to state changes
  });
  store.$onAction(() => {
    // React to actions
  });
});

Nuxt Plugin

Create a Nuxt plugin to add Pinia plugins:

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