Create a pinia (the root store) and pass it to app:
```js
+// Vue 3
+import { createApp } from 'vue'
import { createPinia } from 'pinia'
+import App from './App.vue'
-app.use(createPinia())
+const pinia = createPinia()
+const app = createApp(App)
+
+app.use(pinia)
+app.mount('#app')
+```
+
+```js
+// Vue 2
+import { createPinia, PiniaVuePlugin } from 'pinia'
+
+Vue.use(PiniaVuePlugin)
+const pinia = createPinia()
+
+new Vue({
+ el: '#app',
+ // other options...
+ // ...
+ // note the same `pinia` instance can be used across multiple Vue apps on
+ // the same page
+ pinia,
+})
```
### Create a Store
import { defineStore, skipHydrate } from 'pinia'
import { useEyeDropper, useLocalStorage } from '@vueuse/core'
-const useColorStore = defineStore('colors', () => {
+export const useColorStore = defineStore('colors', () => {
const { isSupported, open, sRGBHex } = useEyeDropper()
const lastColor = useLocalStorage('lastColor', sRGBHex)
// ...
Actions are the equivalent of [methods](https://v3.vuejs.org/guide/data-methods.html#methods) in components. They can be defined with the `actions` property in `defineStore()` and **they are perfect to define business logic**:
```js
-export const useStore = defineStore('main', {
+export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
+ // since we rely on `this`, we cannot use an arrow function
increment() {
this.count++
},
```js
export default defineComponent({
setup() {
- const main = useMainStore()
+ const store = useCounterStore()
// call the action as a method of the store
- main.randomizeCounter()
+ store.randomizeCounter()
return {}
},
```js
export default {
setup() {
- const store = useStore()
+ const store = useCounterStore()
store.randomizeCounter()
},
import { defineStore } from 'pinia',
-const useCounterStore = defineStore('counter', {
+export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
### With `setup()`
-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!
+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
import { useCounterStore } from '../stores/counter'
// same as calling from store.increment()
...mapActions(useCounterStore, ['increment'])
// same as above but registers it as this.myOwnName()
- ...mapActions(useCounterStore, { myOwnName: 'doubleCount' }),
+ ...mapActions(useCounterStore, { myOwnName: 'increment' }),
},
}
```
unsubscribe()
```
-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 want to keep them after the component is unmounted, pass `true` as the second argument to _detach_ the _action subscription_ from the current component:
+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 after the component is unmounted
+ // this subscription will be kept even after the component is unmounted
someStore.$onAction(callback, true)
// ...
Getters are exactly the equivalent of [computed values](https://vuejs.org/guide/essentials/computed.html) for the state of a Store. They can be defined with the `getters` property in `defineStore()`. They receive the `state` as the first parameter **to encourage** the usage of arrow function:
```js
-export const useStore = defineStore('main', {
+export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
Most of the time, getters will only rely on the state, however, they might need to use other getters. Because of this, we can get access to the _whole store instance_ through `this` when defining a regular function **but it is necessary to define the type of the return type (in TypeScript)**. This is due to a known limitation in TypeScript and **doesn't affect getters defined with an arrow function nor getters not using `this`**:
```ts
-export const useStore = defineStore('main', {
+export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
<script>
export default {
setup() {
- const store = useStore()
+ const store = useCounterStore()
return { store }
},
As with computed properties, you can combine multiple getters. Access any other getter via `this`. Even if you are not using TypeScript, you can hint your IDE for types with the [JSDoc](https://jsdoc.app/tags-returns.html):
```js
-export const useStore = defineStore('main', {
+export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
```js
export default {
setup() {
- const store = useStore()
+ const store = useCounterStore()
store.count = 3
store.doubleCount // 6
```js {2-10}
export const useCounterStore = defineStore('counter', {
- state: () => ({ count: 0 }),
+ state: () => ({ count: 0, name: 'Eduardo' }),
getters: {
- double: (state) => state.count * 2,
+ doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
```js
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
+ const name = ref('Eduardo')
+ const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
- return { count, increment }
+ return { count, name, doubleCount, increment }
})
```
## Using the store
-We are _defining_ a store because the store won't be created until `useStore()` is called inside of `setup()`:
+We are _defining_ a store because the store won't be created until `use...Store()` is called inside of `setup()`:
```js
import { useCounterStore } from '@/stores/counter'
}
```
-You can define as many stores as you want and **you should define each store in a different file** to get the most out of pinia (like automatically allow your bundle to code split and TypeScript inference).
-
+:::tip
If you are not using `setup` components yet, [you can still use Pinia with _map helpers_](../cookbook/options-api.md).
+:::
+
+You can define as many stores as you want and **you should define each store in a different file** to get the most out of pinia (like automatically allow your bundler to code split and TypeScript inference).
Once the store is instantiated, you can access any property defined in `state`, `getters`, and `actions` directly on the store. We will see these in detail in the next pages but autocompletion will help you.
// it's the same as destructuring from `props`
const { name, doubleCount } = store
- name // "eduardo"
- doubleCount // 2
+ name // "Eduardo"
+ doubleCount // 0
+
+ setTimeout(() => {
+ store.increment()
+ }, 1000)
return {
- // will always be "eduardo"
+ // will always be "Eduardo"
name,
- // will always be 2
+ // will always be 0
doubleCount,
- // this one will be reactive
+ // will also always be 0
+ doubleNumber: store.doubleCount,
+
+ // ✅ this one will be reactive
doubleValue: computed(() => store.doubleCount),
}
},
```js
import { defineStore } from 'pinia'
-const useStore = defineStore('storeId', {
+export const useStore = defineStore('storeId', {
// arrow function recommended for full type inference
state: () => {
return {
You don't need to do much in order to make your state compatible with TS: make sure [`strict`](https://www.typescriptlang.org/tsconfig#strict), or at the very least, [`noImplicitThis`](https://www.typescriptlang.org/tsconfig#noImplicitThis), are enabled and Pinia will infer the type of your state automatically! However, there are a few cases where you should give it a hand with some casting:
```ts
-const useStore = defineStore('storeId', {
+export const useUserStore = defineStore('user', {
state: () => {
return {
// for initially empty lists
user: UserInfo | null
}
-const useStore = defineStore('storeId', {
+export const useUserStore = defineStore('user', {
state: (): State => {
return {
userList: [],
import { defineStore } from 'pinia'
-const useCounterStore = defineStore('counter', {
+export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
})
```
-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 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:
+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 after the component is unmounted
+ // this subscription will be kept even after the component is unmounted
someStore.$subscribe(callback, { detached: true })
// ...