Pinia Internals — Series 2

Preview — 3 of 10 questions

What does destructuring { store, options } give a Pinia plugin access to?

javascript
function myPlugin({ store, app, pinia, options }) {
  console.log(store.$id)               // the store being set up right now
  console.log(options.customLabel)      // a custom field on THIS store's defineStore call
}

// stores/counter.js
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  customLabel: 'My Counter', // not a real Pinia option — just custom data for the plugin
})
AA Pinia plugin function is called once per store, receiving a context object whose options field is the exact raw object originally passed to that specific store's defineStore(id, options) call — this lets a plugin read arbitrary custom fields (like customLabel here) that aren't part of Pinia's own recognized options, giving each store a way to configure plugin behavior on a per-store basis
Boptions always refers to the plugin's own configuration, passed when the plugin itself was registered via pinia.use(myPlugin, options) — it has nothing to do with any individual store's defineStore() call
Cstore inside a plugin function is a plain, non-reactive snapshot of the store's state at the moment the plugin runs — it doesn't reflect later changes
DPlugins can only access pinia and app — store and options are not available inside a plugin function at all

Why does TypeScript recognize store.label after this module augmentation, when the plugin adds it dynamically at runtime?

javascript
// plugins/labelPlugin.ts
import 'pinia'

declare module 'pinia' {
  export interface PiniaCustomProperties {
    label: string
  }
}

export function labelPlugin({ store, options }: PiniaPluginContext) {
  store.label = options.customLabel ?? 'Untitled'
}
APiniaCustomProperties is a dedicated interface Pinia exposes specifically for this purpose — declaration merging into it tells TypeScript "every store instance also has these additional properties," matching what a plugin actually adds at runtime. Without this augmentation, store.label would be a TypeScript error (label doesn't exist on the store's type) even though it genuinely exists at runtime after the plugin runs
BThis is invalid — plugins can only add properties Pinia already recognizes; arbitrary new properties like label can never be added
CPiniaCustomProperties requires the plugin itself to be written in TypeScript — a JavaScript plugin can never have its added properties typed this way
DThis augmentation changes the runtime behavior of useCounterStore() — without it, store.label would actually be undefined at runtime, not just untyped

In what order does myPlugin run relative to when useCounterStore() and useCartStore() are each first called?

javascript
const pinia = createPinia()
pinia.use(myPlugin) // registered once, before any store is used

app.use(pinia)

// ... much later, in different components ...
const counterStore = useCounterStore() // first call anywhere in the app
const cartStore = useCartStore()        // first call anywhere in the app, even later
AmyPlugin only runs for stores that already existed at the moment pinia.use(myPlugin) was called — counterStore and cartStore, both created later, never have the plugin applied to them
BmyPlugin runs exactly once, globally, at the moment pinia.use() is called — it has no per-store awareness at all
CA plugin registered via pinia.use() applies to every store, including ones created after the plugin was registered — Pinia runs the plugin function once for each store the first time that store is actually instantiated (its first useXStore() call), whenever that happens to occur, not just for stores that already existed at registration time
DPlugins must be re-registered individually for each store via a plugins: [myPlugin] option inside that specific store's own defineStore() call

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.