Lead Instructor @ Vue School
Full Stack developer (10 years)
Husband and Father
762 Lessons | 63 Hours | 40 Courses
Eduardo uses
VS Code
.vscode/settings.json
Pinia is a monorepo
Distributed
via NPM
Uses Prettier
for Formatting
MIT
License
versions getting security patches
tested with Jest
Lerna mono-repo tooling
https://github.com/lerna/lerna
docs deployed
to Netlify
manages deps with pnpm
automated dependency updates
Uses TypeScript
The packages
docs built with Vitepress
docs built with Vitepress
includes a Nuxt module
playground important iteration
automated package size report
helpers for unit testing
createTestingPinia
the main package
Built in devtools support
Used to install Pinia with Vue
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
app.use(createPinia())
Used to install Pinia with Vue
// createPinia.ts
import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'
import { ref, App, markRaw, effectScope, isVue2, Ref } from 'vue-demi'
import { registerPiniaDevtools, devtoolsPlugin } from './devtools'
import { IS_CLIENT } from './env'
import { StateTree, StoreGeneric } from './types'
/**
* Creates a Pinia instance to be used by the application
*/
export function createPinia(): Pinia {
const scope = effectScope(true)
// NOTE: here we could check the window object for a state and directly set it
// if there is anything like it with Vue 3 SSR
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = []
// plugins added before calling app.use(pinia)
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
// this allows calling useStore() outside of a component setup after
// installing pinia's plugin
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
/* istanbul ignore else */
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
},
_p,
// it's actually undefined here
// @ts-expect-error
_a: null,
_e: scope,
_s: new Map<string, StoreGeneric>(),
state,
})
// pinia devtools rely on dev only features so they cannot be forced unless
// the dev build of Vue is used
// We also don't need devtools in test mode
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
}
// rootStore.ts
/**
* Every application must own its own pinia to be able to create stores
*/
export interface Pinia {
install: (app: App) => void
/**
* root state
*/
state: Ref<Record<string, StateTree>>
/**
* Adds a store plugin to extend every store
*
* @param plugin - store plugin to add
*/
use(plugin: PiniaPlugin): Pinia
/**
* Installed store plugins
*
* @internal
*/
_p: PiniaPlugin[]
/**
* App linked to this Pinia instance
*
* @internal
*/
_a: App
/**
* Effect scope the pinia is attached to
*
* @internal
*/
_e: EffectScope
/**
* Registry of stores used by this pinia.
*
* @internal
*/
_s: Map<string, StoreGeneric>
/**
* Added by `createTestingPinia()` to bypass `useStore(pinia)`.
*
* @internal
*/
_testing?: boolean
}
// rootStore.ts
/**
* Every application must own its own pinia to be able to create stores
*/
export interface Pinia {
install: (app: App) => void
/**
* root state
*/
state: Ref<Record<string, StateTree>>
/**
* Adds a store plugin to extend every store
*
* @param plugin - store plugin to add
*/
use(plugin: PiniaPlugin): Pinia
/**
* Installed store plugins
*
* @internal
*/
_p: PiniaPlugin[]
/**
* App linked to this Pinia instance
*
* @internal
*/
_a: App
/**
* Effect scope the pinia is attached to
*
* @internal
*/
_e: EffectScope
/**
* Registry of stores used by this pinia.
*
* @internal
*/
_s: Map<string, StoreGeneric>
/**
* Added by `createTestingPinia()` to bypass `useStore(pinia)`.
*
* @internal
*/
_testing?: boolean
}
// rootStore.ts
/**
* Every application must own its own pinia to be able to create stores
*/
export interface Pinia {
install: (app: App) => void
/**
* root state
*/
state: Ref<Record<string, StateTree>>
/**
* Adds a store plugin to extend every store
*
* @param plugin - store plugin to add
*/
use(plugin: PiniaPlugin): Pinia
/**
* Installed store plugins
*
* @internal
*/
_p: PiniaPlugin[]
/**
* App linked to this Pinia instance
*
* @internal
*/
_a: App
/**
* Effect scope the pinia is attached to
*
* @internal
*/
_e: EffectScope
/**
* Registry of stores used by this pinia.
*
* @internal
*/
_s: Map<string, StoreGeneric>
/**
* Added by `createTestingPinia()` to bypass `useStore(pinia)`.
*
* @internal
*/
_testing?: boolean
}
TS Record Utility Type
// rootStore.ts
/**
* Every application must own its own pinia to be able to create stores
*/
export interface Pinia {
install: (app: App) => void
/**
* root state
*/
state: Ref<Record<string, StateTree>>
/**
* Adds a store plugin to extend every store
*
* @param plugin - store plugin to add
*/
use(plugin: PiniaPlugin): Pinia
/**
* Installed store plugins
*
* @internal
*/
_p: PiniaPlugin[]
/**
* App linked to this Pinia instance
*
* @internal
*/
_a: App
/**
* Effect scope the pinia is attached to
*
* @internal
*/
_e: EffectScope
/**
* Registry of stores used by this pinia.
*
* @internal
*/
_s: Map<string, StoreGeneric>
/**
* Added by `createTestingPinia()` to bypass `useStore(pinia)`.
*
* @internal
*/
_testing?: boolean
}
// types.ts
export type StateTree = Record<string | number | symbol, any>
TS Record Utility Type
// rootStore.ts
/**
* Every application must own its own pinia to be able to create stores
*/
export interface Pinia {
install: (app: App) => void
/**
* root state
*/
state: Ref<Record<string, StateTree>>
/**
* Adds a store plugin to extend every store
*
* @param plugin - store plugin to add
*/
use(plugin: PiniaPlugin): Pinia
/**
* Installed store plugins
*
* @internal
*/
_p: PiniaPlugin[]
/**
* App linked to this Pinia instance
*
* @internal
*/
_a: App
/**
* Effect scope the pinia is attached to
*
* @internal
*/
_e: EffectScope
/**
* Registry of stores used by this pinia.
*
* @internal
*/
_s: Map<string, StoreGeneric>
/**
* Added by `createTestingPinia()` to bypass `useStore(pinia)`.
*
* @internal
*/
_testing?: boolean
}
export const piniaSymbol = (
__DEV__ ? Symbol('pinia') : /* istanbul ignore next */ Symbol()
) as InjectionKey<Pinia>
/**
* Context argument passed to Pinia plugins.
*/
export interface PiniaPluginContext<
Id extends string = string,
S extends StateTree = StateTree,
G /* extends _GettersTree<S> */ = _GettersTree<S>,
A /* extends _ActionsTree */ = _ActionsTree
> {
/**
* pinia instance.
*/
pinia: Pinia
/**
* Current app created with `Vue.createApp()`.
*/
app: App
/**
* Current store being extended.
*/
store: Store<Id, S, G, A>
/**
* Initial options defining the store when calling `defineStore()`.
*/
options: DefineStoreOptionsInPlugin<Id, S, G, A>
}
/**
* Plugin to extend every store.
*/
export interface PiniaPlugin {
/**
* Plugin to extend every store. Returns an object to extend the store or
* nothing.
*
* @param context - Context
*/
(context: PiniaPluginContext): Partial<
PiniaCustomProperties & PiniaCustomStateProperties
> | void
}
https://tsdoc.org
// createPinia.ts
import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'
import { ref, App, markRaw, effectScope, isVue2, Ref } from 'vue-demi'
import { registerPiniaDevtools, devtoolsPlugin } from './devtools'
import { IS_CLIENT } from './env'
import { StateTree, StoreGeneric } from './types'
/**
* Creates a Pinia instance to be used by the application
*/
export function createPinia(): Pinia {
const scope = effectScope(true)
// NOTE: here we could check the window object for a state and directly set it
// if there is anything like it with Vue 3 SSR
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = []
// plugins added before calling app.use(pinia)
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
// this allows calling useStore() outside of a component setup after
// installing pinia's plugin
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
/* istanbul ignore else */
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
},
_p,
// it's actually undefined here
// @ts-expect-error
_a: null,
_e: scope,
_s: new Map<string, StoreGeneric>(),
state,
})
// pinia devtools rely on dev only features so they cannot be forced unless
// the dev build of Vue is used
// We also don't need devtools in test mode
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
}
// createPinia.ts
import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'
import { ref, App, markRaw, effectScope, isVue2, Ref } from 'vue-demi'
import { registerPiniaDevtools, devtoolsPlugin } from './devtools'
import { IS_CLIENT } from './env'
import { StateTree, StoreGeneric } from './types'
/**
* Creates a Pinia instance to be used by the application
*/
export function createPinia(): Pinia {
const scope = effectScope(true)
// NOTE: here we could check the window object for a state and directly set it
// if there is anything like it with Vue 3 SSR
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = []
// plugins added before calling app.use(pinia)
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
// this allows calling useStore() outside of a component setup after
// installing pinia's plugin
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
/* istanbul ignore else */
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
},
_p,
// it's actually undefined here
// @ts-expect-error
_a: null,
_e: scope,
_s: new Map<string, StoreGeneric>(),
state,
})
// pinia devtools rely on dev only features so they cannot be forced unless
// the dev build of Vue is used
// We also don't need devtools in test mode
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
}
Handle Pinia Plugins
// createPinia.ts
import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'
import { ref, App, markRaw, effectScope, isVue2, Ref } from 'vue-demi'
import { registerPiniaDevtools, devtoolsPlugin } from './devtools'
import { IS_CLIENT } from './env'
import { StateTree, StoreGeneric } from './types'
/**
* Creates a Pinia instance to be used by the application
*/
export function createPinia(): Pinia {
const scope = effectScope(true)
// NOTE: here we could check the window object for a state and directly set it
// if there is anything like it with Vue 3 SSR
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = []
// plugins added before calling app.use(pinia)
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
// this allows calling useStore() outside of a component setup after
// installing pinia's plugin
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
/* istanbul ignore else */
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
},
_p,
// it's actually undefined here
// @ts-expect-error
_a: null,
_e: scope,
_s: new Map<string, StoreGeneric>(),
state,
})
// pinia devtools rely on dev only features so they cannot be forced unless
// the dev build of Vue is used
// We also don't need devtools in test mode
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
}
// rootStore.ts
export let activePinia: Pinia | undefined
export const setActivePinia = (pinia: Pinia | undefined) =>
(activePinia = pinia)
// createPinia.ts
import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'
import { ref, App, markRaw, effectScope, isVue2, Ref } from 'vue-demi'
import { registerPiniaDevtools, devtoolsPlugin } from './devtools'
import { IS_CLIENT } from './env'
import { StateTree, StoreGeneric } from './types'
/**
* Creates a Pinia instance to be used by the application
*/
export function createPinia(): Pinia {
const scope = effectScope(true)
// NOTE: here we could check the window object for a state and directly set it
// if there is anything like it with Vue 3 SSR
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = []
// plugins added before calling app.use(pinia)
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
// this allows calling useStore() outside of a component setup after
// installing pinia's plugin
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
/* istanbul ignore else */
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
},
_p,
// it's actually undefined here
// @ts-expect-error
_a: null,
_e: scope,
_s: new Map<string, StoreGeneric>(),
state,
})
// pinia devtools rely on dev only features so they cannot be forced unless
// the dev build of Vue is used
// We also don't need devtools in test mode
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
}
// createPinia.ts
import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'
import { ref, App, markRaw, effectScope, isVue2, Ref } from 'vue-demi'
import { registerPiniaDevtools, devtoolsPlugin } from './devtools'
import { IS_CLIENT } from './env'
import { StateTree, StoreGeneric } from './types'
/**
* Creates a Pinia instance to be used by the application
*/
export function createPinia(): Pinia {
const scope = effectScope(true)
// NOTE: here we could check the window object for a state and directly set it
// if there is anything like it with Vue 3 SSR
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = []
// plugins added before calling app.use(pinia)
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
// this allows calling useStore() outside of a component setup after
// installing pinia's plugin
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
/* istanbul ignore else */
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
},
_p,
// it's actually undefined here
// @ts-expect-error
_a: null,
_e: scope,
_s: new Map<string, StoreGeneric>(),
state,
})
// pinia devtools rely on dev only features so they cannot be forced unless
// the dev build of Vue is used
// We also don't need devtools in test mode
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
}
// createPinia.ts
import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'
import { ref, App, markRaw, effectScope, isVue2, Ref } from 'vue-demi'
import { registerPiniaDevtools, devtoolsPlugin } from './devtools'
import { IS_CLIENT } from './env'
import { StateTree, StoreGeneric } from './types'
/**
* Creates a Pinia instance to be used by the application
*/
export function createPinia(): Pinia {
const scope = effectScope(true)
// NOTE: here we could check the window object for a state and directly set it
// if there is anything like it with Vue 3 SSR
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = []
// plugins added before calling app.use(pinia)
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
// this allows calling useStore() outside of a component setup after
// installing pinia's plugin
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
/* istanbul ignore else */
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
},
_p,
// it's actually undefined here
// @ts-expect-error
_a: null,
_e: scope,
_s: new Map<string, StoreGeneric>(),
state,
})
// pinia devtools rely on dev only features so they cannot be forced unless
// the dev build of Vue is used
// We also don't need devtools in test mode
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
}
Grab a FREE limited edition Vue School t-shirt