Replace any usage of the type `GenericStore` with `StoreGeneric`. This is the new generic store type that should accept any kind of store. If you were writing functions using the type `Store` without passing its generics (e.g. `Store<Id, State, Getters, Actions>`), you should also use `StoreGeneric` as the `Store` type without generics creates an empty store type.
-```diff
--function takeAnyStore(store: Store) {}
-+function takeAnyStore(store: StoreGeneric) {}
+```ts
+function takeAnyStore(store: Store) {} // [!code --]
+function takeAnyStore(store: StoreGeneric) {} // [!code ++]
--function takeAnyStore(store: GenericStore) {}
-+function takeAnyStore(store: StoreGeneric) {}
+function takeAnyStore(store: GenericStore) {} // [!code --]
+function takeAnyStore(store: StoreGeneric) {} // [!code ++]
```
## `DefineStoreOptions` for plugins
If you were writing plugins, using TypeScript, and extending the type `DefineStoreOptions` to add custom options, you should rename it to `DefineStoreOptionsBase`. This type will apply to both setup and options stores.
-```diff
- declare module 'pinia' {
-- export interface DefineStoreOptions<S, Store> {
-+ export interface DefineStoreOptionsBase<S, Store> {
- debounce?: {
- [k in keyof StoreActions<Store>]?: number
- }
- }
- }
+```ts
+declare module 'pinia' {
+ export interface DefineStoreOptions<S, Store> { // [!code --]
+ export interface DefineStoreOptionsBase<S, Store> { // [!code ++]
+ debounce?: {
+ [k in keyof StoreActions<Store>]?: number
+ }
+ }
+}
```
## `PiniaStorePlugin` was renamed
The type `PiniaStorePlugin` was renamed to `PiniaPlugin`.
-```diff
--import { PiniaStorePlugin } from 'pinia'
-+import { PiniaPlugin } from 'pinia'
+```ts
+import { PiniaStorePlugin } from 'pinia' // [!code --]
+import { PiniaPlugin } from 'pinia' // [!code ++]
--const piniaPlugin: PiniaStorePlugin = () => {
-+const piniaPlugin: PiniaPlugin = () => {
- // ...
- }
+const piniaPlugin: PiniaStorePlugin = () => { // [!code --]
+const piniaPlugin: PiniaPlugin = () => { // [!code ++]
+ // ...
+}
```
**Note this change can only be done after upgrading to the latest version of Pinia without deprecations**.
- If you are using Vue CLI 4.x, upgrade your dependencies. This should include the fix below.
- If upgrading is not possible for you, add this to your `vue.config.js`:
+
```js
// vue.config.js
module.exports = {
},
}
```
+
- If you are manually handling webpack, you will have to let it know how to handle `.mjs` files:
+
```js
// webpack.config.js
module.exports = {
If you are using Nuxt, pinia has now it's dedicated Nuxt package 🎉. Install it with:
-```shell
+```bash
npm i @pinia/nuxt
# or with yarn
yarn add @pinia/nuxt
Then adapt your `nuxt.config.js` and your `tsconfig.json` if you are using TypeScript:
-```diff
- // nuxt.config.js
- module.exports {
- buildModules: [
- '@nuxtjs/composition-api/module',
-- 'pinia/nuxt',
-+ '@pinia/nuxt',
- ],
- }
+```js
+// nuxt.config.js
+module.exports {
+ buildModules: [
+ '@nuxtjs/composition-api/module',
+ 'pinia/nuxt', // [!code --]
+ '@pinia/nuxt', // [!code ++]
+ ],
+}
```
-```diff
- // tsconfig.json
- {
- "types": [
- // ...
-- "pinia/nuxt/types"
-+ "@pinia/nuxt"
- ]
- }
+```json
+// tsconfig.json
+{
+ "types": [
+ // ...
+ "pinia/nuxt/types" // [!code --]
+ "@pinia/nuxt" // [!code ++]
+ ]
+}
```
It is also recommended to give [the dedicated Nuxt section](../ssr/nuxt.md) a read.
You are also completely free to set whatever arguments you want and return anything. When calling actions, everything will be automatically inferred!
-Actions are invoked like methods:
-
-```js
-export default defineComponent({
- setup() {
- const store = useCounterStore()
- // call the action as a method of the store
- store.randomizeCounter()
-
- return {}
- },
-})
+Actions are invoked like function or regular methods:
+
+```vue
+<script setup>
+const store = useCounterStore()
+// call the action as a method of the store
+store.randomizeCounter()
+</script>
+
+<template>
+ <!-- Even on the template -->
+ <button @click="store.randomizeCounter()">Randomize</button>
+</template>
```
## Accessing other stores actions
})
```
-## Usage with `setup()`
-
-You can directly call any action as a method of the store:
-
-```js
-export default {
- setup() {
- const store = useCounterStore()
-
- store.randomizeCounter()
- },
-}
-```
-
## Usage with the Options API
<VueSchoolLink
While Composition API is not for everyone, the `setup()` hook can make using Pinia easier to work within the Options API. No extra map helper functions needed!
-```js
+```vue
+<script>
import { useCounterStore } from '../stores/counter'
-export default {
+export default defineComponent({
setup() {
const counterStore = useCounterStore()
console.log('New Count:', this.counterStore.count)
},
},
-}
+})
+</script>
```
### Without `setup()`
By default, _action subscriptions_ are bound to the component where they are added (if the store is inside a component's `setup()`). Meaning, they will be automatically removed when the component is unmounted. If you also want to keep them after the component is unmounted, pass `true` as the second argument to _detach_ the _action subscription_ from the current component:
-```js
-export default {
- setup() {
- const someStore = useSomeStore()
-
- // this subscription will be kept even after the component is unmounted
- someStore.$onAction(callback, true)
+```vue
+<script setup>
+const someStore = useSomeStore()
- // ...
- },
-}
+// this subscription will be kept even after the component is unmounted
+someStore.$onAction(callback, true)
+</script>
```
Then you can access the getter directly on the store instance:
```vue
+<script setup>
+import { useCounterStore } from './counterStore'
+
+const store = useCounterStore()
+</script>
+
<template>
<p>Double count is {{ store.doubleCount }}</p>
</template>
-
-<script>
-export default {
- setup() {
- const store = useCounterStore()
-
- return { store }
- },
-}
-</script>
```
## Accessing other getters
and use in component:
```vue
-<script>
-export default {
- setup() {
- const store = useStore()
+<script setup>
+import { useUserListStore } from './store'
- return { getUserById: store.getUserById }
- },
-}
+const userList = useUserListStore()
+const { getUserById } = storeToRefs(userList)
+// note you will have to use `getUserById.value` to access
+// the function within the <script setup>
</script>
<template>
You can directly access any getter as a property of the store (exactly like state properties):
-```js
-export default {
- setup() {
- const store = useCounterStore()
+```vue
+<script setup>
+const store = useCounterStore()
- store.count = 3
- store.doubleCount // 6
- },
-}
+store.count = 3
+store.doubleCount // 6
+</script>
```
## Usage with the Options API
While Composition API is not for everyone, the `setup()` hook can make using Pinia easier to work with in the Options API. No extra map helper functions needed!
-```js
+```vue
+<script>
import { useCounterStore } from '../stores/counter'
-export default {
+export default defineComponent({
setup() {
const counterStore = useCounterStore()
+ // **only return the whole store** instead of destructuring
return { counterStore }
},
computed: {
return this.counterStore.doubleCount * 2
},
},
-}
+})
+</script>
```
+This is useful while migrating a component from the Options API to the Composition API but **should only be a migration step**, always try not to mix both API styles within the same component.
+
### Without `setup()`
You can use the same `mapState()` function used in the [previous section of state](./state.md#options-api) to map to getters:
```js
import { defineStore } from 'pinia'
-// You can name the return value of `defineStore()` anything you want,
-// but it's best to use the name of the store and surround it with `use`
+// You can name the return value of `defineStore()` anything you want,
+// but it's best to use the name of the store and surround it with `use`
// and `Store` (e.g. `useUserStore`, `useCartStore`, `useProductStore`)
// the first argument is a unique id of the store across your application
export const useAlertsStore = defineStore('alerts', {
## Using the store
-We are _defining_ a store because the store won't be created until `use...Store()` is called inside of `setup()`:
+We are _defining_ a store because the store won't be created until `use...Store()` is called within a component `<script setup>` (or within `setup()` **like all composables**):
-```js
+```vue
+<script setup>
import { useCounterStore } from '@/stores/counter'
-export default {
- setup() {
- const store = useCounterStore()
-
- return {
- // you can return the whole store instance to use it in the template
- store,
- }
- },
-}
+// access the `store` variable anywhere in the component ✨
+const store = useCounterStore()
+</script>
```
:::tip
Note that `store` is an object wrapped with `reactive`, meaning there is no need to write `.value` after getters but, like `props` in `setup`, **we cannot destructure it**:
-```js
-export default defineComponent({
- setup() {
- const store = useCounterStore()
- // ❌ This won't work because it breaks reactivity
- // it's the same as destructuring from `props`
- const { name, doubleCount } = store
-
- name // "Eduardo"
- doubleCount // 0
-
- setTimeout(() => {
- store.increment()
- }, 1000)
-
- return {
- // will always be "Eduardo"
- name,
- // will always be 0
- doubleCount,
- // will also always be 0
- doubleNumber: store.doubleCount,
-
- // ✅ this one will be reactive
- doubleValue: computed(() => store.doubleCount),
- }
- },
-})
+```vue
+<script setup>
+const store = useCounterStore()
+// ❌ This won't work because it breaks reactivity
+// it's the same as destructuring from `props`
+const { name, doubleCount } = store
+
+name // will always be "Eduardo"
+doubleCount // will always be 0
+
+setTimeout(() => {
+ store.increment()
+}, 1000)
+
+// ✅ this one will be reactive
+// 💡 but you could also just use `store.doubleCount` directly
+const doubleValue = computed(() => store.doubleCount)
+</script>
```
In order to extract properties from the store while keeping its reactivity, you need to use `storeToRefs()`. It will create refs for every reactive property. This is useful when you are only using state from the store but not calling any action. Note you can destructure actions directly from the store as they are bound to the store itself too:
-```js
+```vue
+<script setup>
import { storeToRefs } from 'pinia'
-export default defineComponent({
- setup() {
- const store = useCounterStore()
- // `name` and `doubleCount` are reactive refs
- // This will also create refs for properties added by plugins
- // but skip any action or non reactive (non ref/reactive) property
- const { name, doubleCount } = storeToRefs(store)
- // the increment action can just be extracted
- const { increment } = store
-
- return {
- name,
- doubleCount,
- increment,
- }
- },
-})
+const store = useCounterStore()
+// `name` and `doubleCount` are reactive refs
+// This will also extract refs for properties added by plugins
+// but skip any action or non reactive (non ref/reactive) property
+const { name, doubleCount } = storeToRefs(store)
+// the increment action can just be destructured
+const { increment } = store
+</script>
```
```js
import { createPinia } from 'pinia'
-// add a property named `secret` to every store that is created after this plugin is installed
-// this could be in a different file
+// add a property named `secret` to every store that is created
+// after this plugin is installed this could be in a different file
function SecretPiniaPlugin() {
return { secret: 'the cake is a lie' }
}
By default, _state subscriptions_ are bound to the component where they are added (if the store is inside a component's `setup()`). Meaning, they will be automatically removed when the component is unmounted. If you also want to keep them after the component is unmounted, pass `{ detached: true }` as the second argument to _detach_ the _state subscription_ from the current component:
-```js
-export default {
- setup() {
- const someStore = useSomeStore()
-
- // this subscription will be kept even after the component is unmounted
- someStore.$subscribe(callback, { detached: true })
+```vue
+<script setup>
+const someStore = useSomeStore()
- // ...
- },
-}
+// this subscription will be kept even after the component is unmounted
+someStore.$subscribe(callback, { detached: true })
+</script>
```
:::tip
-You can watch the whole state on the `pinia` instance:
+You can _watch_ the whole state on the `pinia` instance with a single `watch()`:
```js
watch(
And then you _use_ it in a component:
-```js
+```vue
+<script setup>
import { useCounterStore } from '@/stores/counter'
-export default {
- setup() {
- const counter = useCounterStore()
+const counter = useCounterStore()
- counter.count++
- // with autocompletion ✨
- counter.$patch({ count: counter.count + 1 })
- // or using an action instead
- counter.increment()
- },
-}
+counter.count++
+// with autocompletion ✨
+counter.$patch({ count: counter.count + 1 })
+// or using an action instead
+counter.increment()
+</script>
+
+<template>
+ <!-- Access the state directly from the store -->
+ <div>Current Count: {{ counter.count }}</div>
+</template>
```
You can even use a function (similar to a component `setup()`) to define a Store for more advanced use cases:
// ...
})
-export default {
+export default defineComponent({
computed: {
// other computed properties
// ...
// gives access to this.increment()
...mapActions(useCounterStore, ['increment']),
},
-}
+})
```
You will find more information about each _map helper_ in the core concepts.
Creating stores with Pinia should work out of the box for SSR as long as you call your `useStore()` functions at the top of `setup` functions, `getters` and `actions`:
-```js
-export default defineComponent({
- setup() {
- // this works because pinia knows what application is running inside of
- // `setup()`
- const main = useMainStore()
- return { main }
- },
-})
+```vue
+<script setup>
+// this works because pinia knows what application is running inside of
+// `setup`
+const main = useMainStore()
+</script>
```
## Using the store outside of `setup()`
}
```
+Note you don't need to do anything special when using `onServerPrefetch()`:
+
+```vue
+<script setup>
+const store = useStore()
+onServerPrefetch(async () => {
+ // ✅ this will work
+ await store.fetchData()
+})
+</script>
+```
+
## State hydration
To hydrate the initial state, you need to make sure the rootState is included somewhere in the HTML for Pinia to pick it up later on. Depending on what you are using for SSR, **you should escape the state for security reasons**. We recommend using [@nuxt/devalue](https://github.com/nuxt-contrib/devalue) which is the one used by Nuxt.js:
npm install pinia @pinia/nuxt
```
-:::tip
+:::tip
If you're using npm, you might encounter an _ERESOLVE unable to resolve dependency tree_ error. In that case, add the following to your `package.json`:
```js
"vue": "latest"
}
```
+
:::
We supply a _module_ to handle everything for you, you only need to add it to `modules` in your `nuxt.config.js` file:
}
```
+As with `onServerPrefetch()`, you don't need to do anything special if you want to call a store action within `asyncData()`:
+
+```vue
+<script setup>
+const store = useStore()
+const { data } = await useAsyncData('user', () => store.fetchUser())
+</script>
+```
+
## Auto imports
By default `@pinia/nuxt` exposes one single auto import: `usePinia()`, which is similar to `getActivePinia()` but works better with Nuxt. You can add auto imports to make your life easier: