Pinia: a store
Pinia is the official library for shared state in Vue. A store is an object with data and functions that any component can reach without props and events. In the blog the list of posts is needed by the home page, the admin section and the counter in the header — it is convenient to keep it in one store.
Installing
npm install piniaConnect Pinia in src/main.js:
// src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')If you tick Pinia when creating a project, create-vue does this itself and puts an example store into src/stores/counter.js.
Describing a store
Stores go into the src/stores/ folder, one file per topic. Create src/stores/posts.js:
// src/stores/posts.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const usePostsStore = defineStore('posts', () => {
const posts = ref([
{ id: 1, title: 'Hello, Vue', body: 'The first blog post.' },
{ id: 2, title: 'Components', body: 'How to split a page into parts.' },
])
const count = computed(() => posts.value.length)
return { posts, count }
})defineStore takes a unique store name and a function built like <script setup>: ref is the store's data (state), computed are derived values (getters). The function returns everything needed outside. By convention the function name is use + name + Store.
Using it in components
Call usePostsStore() in any component. For example, in the header of src/App.vue:
<!-- src/App.vue -->
<script setup>
import { usePostsStore } from './stores/posts.js'
const postsStore = usePostsStore()
</script>
<template>
<nav>
<RouterLink to="/">Home</RouterLink> |
<RouterLink to="/admin">Admin</RouterLink>
<span>Posts: {{ postsStore.count }}</span>
</nav>
<RouterView />
</template>And in src/views/AdminPostsView.vue:
<!-- src/views/AdminPostsView.vue -->
<script setup>
import { usePostsStore } from '../stores/posts.js'
const postsStore = usePostsStore()
</script>
<template>
<h2>All posts</h2>
<ul>
<li v-for="post in postsStore.posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>The header shows "Posts: 2", and the admin section shows two posts. Both components got the same object: when a post is added, both places update.
Developer tools
The Vue DevTools browser extension has a Pinia tab: it shows all stores and their current data, and you can change values right there.
Common mistake
Destructuring a store: const { posts, count } = usePostsStore(). A store is a reactive object, and destructuring copies the current values, after which the link is lost: the counter stops updating. Access it with a dot — postsStore.count — and if you need destructuring, use storeToRefs from the next lesson.