Skip to content

Plugins API Reference

This section provides a complete API reference for the Pinia plugin system.

Core Plugin API

pinia.use()

Register a plugin to the Pinia instance.

ts
function use(plugin: PiniaPlugin): Pinia

Parameters

  • plugin: PiniaPlugin - The plugin function to register

Returns

  • Type: Pinia
  • Description: The Pinia instance (supports method chaining)

Example

js
import { createPinia } from 'pinia'
import myPlugin from './my-plugin'

const pinia = createPinia()
pinia.use(myPlugin)

// Method chaining
pinia
  .use(plugin1)
  .use(plugin2)
  .use(plugin3)

Plugin Type Definitions

PiniaPlugin

Type definition for plugin functions.

ts
type PiniaPlugin = (context: PiniaPluginContext) => 
  Partial<PiniaCustomProperties & PiniaCustomStateProperties> | void

Parameters

  • context: PiniaPluginContext - Plugin context object

Returns

  • Type: Partial<PiniaCustomProperties & PiniaCustomStateProperties> | void
  • Description: Properties to add to the store, or void

PiniaPluginContext

Plugin context interface.

ts
interface PiniaPluginContext<
  Id extends string = string,
  S extends StateTree = StateTree,
  G extends _GettersTree<S> = _GettersTree<S>,
  A extends _ActionsTree = _ActionsTree
> {
  pinia: Pinia
  app: App
  store: Store<Id, S, G, A>
  options: DefineStoreOptionsInPlugin<Id, S, G, A>
}

Properties

  • pinia: Pinia - Current Pinia instance
  • app: App - Vue application instance
  • store: Store<Id, S, G, A> - Current store instance
  • options: DefineStoreOptionsInPlugin<Id, S, G, A> - Store definition options

Plugin Context Properties

context.pinia

The current Pinia instance.

ts
const pinia: Pinia

Example

js
function myPlugin({ pinia }) {
  // Access Pinia instance
  console.log('Pinia instance:', pinia)
  
  // Access global state
  console.log('Global state:', pinia.state.value)
  
  // Access all stores
  console.log('All stores:', pinia._s)
}

context.app

Vue application instance.

ts
const app: App

Example

js
function myPlugin({ app }) {
  // Access Vue app instance
  console.log('Vue app:', app)
  
  // Access app config
  console.log('App config:', app.config)
  
  // Register global components
  app.component('MyComponent', MyComponent)
}

context.store

Current store instance.

ts
const store: Store<Id, S, G, A>

Example

js
function myPlugin({ store }) {
  // Access store properties
  console.log('Store ID:', store.$id)
  console.log('Store state:', store.$state)
  
  // Listen to state changes
  store.$subscribe((mutation, state) => {
    console.log('State changed:', mutation, state)
  })
  
  // Listen to action execution
  store.$onAction((action) => {
    console.log('Action executed:', action)
  })
}

context.options

Store definition options.

ts
const options: DefineStoreOptionsInPlugin<Id, S, G, A>

Example

js
function myPlugin({ options }) {
  // Access store definition options
  console.log('Store ID:', options.id)
  console.log('State function:', options.state)
  console.log('Getters:', options.getters)
  console.log('Actions:', options.actions)
  
  // Check custom options
  if (options.persist) {
    console.log('Persistence enabled:', options.persist)
  }
}

Plugin Return Values

Adding Properties to Store

Plugins can return objects to add properties to stores.

js
function addPropertiesPlugin() {
  return {
    // Add reactive property
    hello: ref('world'),
    
    // Add computed property
    doubled: computed(() => store.count * 2),
    
    // Add method
    reset() {
      store.$reset()
    },
    
    // Add async method
    async fetchData() {
      const data = await api.getData()
      store.data = data
    }
  }
}

Adding State Properties

js
function addStatePlugin({ store }) {
  return {
    // Add to state
    $state: {
      createdAt: new Date(),
      version: '1.0.0'
    }
  }
}

Common Plugin Patterns

Logger Plugin

js
function createLoggerPlugin(options = {}) {
  return function loggerPlugin({ store }) {
    const { logActions = true, logMutations = true } = options
    
    if (logActions) {
      store.$onAction(({ name, args, after, onError }) => {
        console.log(`🚀 Action "${name}" started`, args)
        
        after((result) => {
          console.log(`✅ Action "${name}" completed`, result)
        })
        
        onError((error) => {
          console.error(`❌ Action "${name}" failed`, error)
        })
      })
    }
    
    if (logMutations) {
      store.$subscribe((mutation, state) => {
        console.log(`🔄 State changed:`, mutation)
        console.log(`📊 New state:`, state)
      })
    }
  }
}

// Usage
pinia.use(createLoggerPlugin({
  logActions: true,
  logMutations: false
}))

Persistence Plugin

js
function createPersistedStatePlugin(options = {}) {
  return function persistedStatePlugin({ store, options: storeOptions }) {
    // Check if persistence is enabled
    if (!storeOptions.persist) return
    
    const {
      key = store.$id,
      storage = localStorage,
      paths = null
    } = typeof storeOptions.persist === 'object' 
      ? storeOptions.persist 
      : {}
    
    // Restore state
    const savedState = storage.getItem(key)
    if (savedState) {
      try {
        const parsed = JSON.parse(savedState)
        if (paths) {
          // Only restore specified paths
          paths.forEach(path => {
            if (parsed[path] !== undefined) {
              store.$state[path] = parsed[path]
            }
          })
        } else {
          // Restore entire state
          store.$patch(parsed)
        }
      } catch (error) {
        console.error('Failed to restore state:', error)
      }
    }
    
    // Listen to state changes and save
    store.$subscribe((mutation, state) => {
      try {
        const toSave = paths 
          ? paths.reduce((acc, path) => {
              acc[path] = state[path]
              return acc
            }, {})
          : state
        
        storage.setItem(key, JSON.stringify(toSave))
      } catch (error) {
        console.error('Failed to save state:', error)
      }
    })
  }
}

// Usage
pinia.use(createPersistedStatePlugin())

Reset Plugin

js
function resetPlugin({ store }) {
  const initialState = JSON.parse(JSON.stringify(store.$state))
  
  return {
    $reset() {
      store.$patch(initialState)
    }
  }
}

// Usage
pinia.use(resetPlugin)

Debug Plugin

js
function createDebugPlugin(options = {}) {
  return function debugPlugin({ store }) {
    if (process.env.NODE_ENV !== 'development') return
    
    const { 
      logLevel = 'info',
      enableTimeTravel = true,
      maxHistorySize = 50 
    } = options
    
    // History tracking
    const history = []
    
    // Track state changes
    store.$subscribe((mutation, state) => {
      const entry = {
        timestamp: Date.now(),
        mutation,
        state: JSON.parse(JSON.stringify(state))
      }
      
      history.push(entry)
      
      // Limit history size
      if (history.length > maxHistorySize) {
        history.shift()
      }
      
      if (logLevel === 'verbose') {
        console.log('🔍 Debug info:', entry)
      }
    })
    
    return {
      // Time travel
      $timeTravel: enableTimeTravel ? (index) => {
        if (history[index]) {
          store.$patch(history[index].state)
        }
      } : undefined,
      
      // Get history
      $getHistory: () => [...history],
      
      // Clear history
      $clearHistory: () => {
        history.length = 0
      }
    }
  }
}

// Usage
pinia.use(createDebugPlugin({
  logLevel: 'verbose',
  enableTimeTravel: true
}))

Advanced Plugin Features

Plugin Communication

js
// Event bus plugin
function createEventBusPlugin() {
  const eventBus = new EventTarget()
  
  return function eventBusPlugin({ store }) {
    return {
      $emit(event, data) {
        eventBus.dispatchEvent(new CustomEvent(event, { detail: data }))
      },
      
      $on(event, handler) {
        eventBus.addEventListener(event, handler)
        return () => eventBus.removeEventListener(event, handler)
      },
      
      $off(event, handler) {
        eventBus.removeEventListener(event, handler)
      }
    }
  }
}

// Usage
pinia.use(createEventBusPlugin())

// In store
const store = useMyStore()
store.$emit('user-updated', { id: 1, name: 'John' })
store.$on('user-updated', (event) => {
  console.log('User updated:', event.detail)
})

Conditional Plugins

js
function createConditionalPlugin(condition, plugin) {
  return function conditionalPlugin(context) {
    if (condition(context)) {
      return plugin(context)
    }
  }
}

// Development-only plugin
const devOnlyPlugin = createConditionalPlugin(
  () => process.env.NODE_ENV === 'development',
  debugPlugin
)

pinia.use(devOnlyPlugin)

// Store-specific plugin
const userStoreOnlyPlugin = createConditionalPlugin(
  ({ store }) => store.$id === 'user',
  userSpecificPlugin
)

pinia.use(userStoreOnlyPlugin)

Plugin Composition

js
function composePlugins(...plugins) {
  return function composedPlugin(context) {
    const results = plugins.map(plugin => plugin(context)).filter(Boolean)
    
    // Merge all plugin return values
    return Object.assign({}, ...results)
  }
}

// Compose multiple plugins
const combinedPlugin = composePlugins(
  loggerPlugin,
  persistedStatePlugin,
  resetPlugin
)

pinia.use(combinedPlugin)

TypeScript Support

Plugin Type Definitions

ts
import { PiniaPluginContext, Store } from 'pinia'

// Define plugin options type
interface MyPluginOptions {
  enabled?: boolean
  prefix?: string
}

// Define plugin properties type
interface MyPluginProperties {
  $myMethod: () => void
  $myProperty: string
}

// Plugin function type
type MyPlugin = (options?: MyPluginOptions) => 
  (context: PiniaPluginContext) => MyPluginProperties

// Implement plugin
const createMyPlugin: MyPlugin = (options = {}) => {
  return ({ store }) => {
    return {
      $myMethod() {
        console.log('My method called')
      },
      $myProperty: options.prefix || 'default'
    }
  }
}

Extending Store Types

ts
// Module declaration
declare module 'pinia' {
  export interface PiniaCustomProperties {
    $myMethod: () => void
    $myProperty: string
  }
  
  export interface PiniaCustomStateProperties {
    createdAt: Date
  }
}

// Now TypeScript knows these properties exist
const store = useMyStore()
store.$myMethod() // ✅ Type safe
store.$myProperty // ✅ Type safe

Plugin Best Practices

1. Naming Conventions

js
// Use $ prefix to avoid conflicts with user properties
function myPlugin() {
  return {
    $myMethod() {}, // ✅ Good
    myMethod() {},  // ❌ May conflict
  }
}

2. Error Handling

js
function safePlugin({ store }) {
  try {
    // Plugin logic
    return {
      $safeMethod() {
        try {
          // Method implementation
        } catch (error) {
          console.error('Plugin method failed:', error)
        }
      }
    }
  } catch (error) {
    console.error('Plugin initialization failed:', error)
    return {}
  }
}

3. Performance Considerations

js
function performantPlugin({ store }) {
  // Avoid expensive operations on every state change
  const debouncedSave = debounce(() => {
    // Expensive save operation
  }, 1000)
  
  store.$subscribe(() => {
    debouncedSave()
  })
}

4. Resource Cleanup

js
function resourcePlugin({ store }) {
  const interval = setInterval(() => {
    // Periodic task
  }, 5000)
  
  // Cleanup when store is disposed
  store.$dispose(() => {
    clearInterval(interval)
  })
}

Released under the MIT License.