Pinia Documentation
website·Indexed 33 days ago
https://pinia.vuejs.kr/The intuitive store library for Vue.js, designed to leverage the Composition API. This documentation covers core concepts such as state, getters, actions, and plugins, as well as advanced usage including Server-Side Rendering (SSR), Nuxt integration, and migration guides from Vuex ≤4 and previous Pinia versions (v1, v2, and 0.0.7).
What's inside Pinia
- Pinia is a store library for Vue that allows state to be shared across components and pages. While simple reactive objects can share state in single-page applications, Pinia is required for Server-Side Rendering (SSR) to avoid cross-request state pollution security vulnerabilities. Key benefits include testing utilities, plugin support, TypeScript/JS autocompletion, SSR support, DevTools integration (timeline tracking, time travel), and Hot Module Replacement (HMR).
Understand Pinia store concepts and usage
A Pinia store is an entity that holds state and business logic independent of the component tree, serving as a global state manager. It consists of three core concepts that map to Vue component options:
- State: Equivalent to
data. - Getters: Equivalent to
computed. - Actions: Equivalent to
methods.
When to use a store: Use stores for data that must be accessible across the entire application (e.g., user profile info in a navigation bar) or data that must persist across multiple pages (e.g., complex multi-step forms).
When to avoid a store: Do not use stores for local data that only affects a single component (e.g., whether a specific element is visible). If a store feels overused, consider using a simple composable or local component state instead.
- State: Equivalent to
Restructure Vuex modules into Pinia stores
In Vuex, the architecture consists of a single store with multiple modules (which can be namespaced or nested). In Pinia, the recommended approach is to convert each Vuex module into its own independent store. Each Pinia store requires a unique
id, which serves a similar purpose to the Vuex namespace. Nested modules should also be converted into individual stores; dependencies between stores are handled by importing one store into another.It is recommended to use a
stores/directory (plural) instead ofstore/to emphasize that Pinia uses multiple stores rather than a single global one. For large projects, you can migrate modules incrementally, as Pinia and Vuex can coexist in the same application.# Vuex Structure (Namespaced) src └── store ├── index.js # Initialization └── modules ├── module1.js # 'module1' namespace └── nested ├── index.js # 'nested' namespace ├── module2.js # 'nested/module2' namespace └── module3.js # 'nested/module3' namespace # Pinia Equivalent (Flat structure, IDs match namespaces) src └── stores ├── index.js # Initialization (optional) ├── module1.js # 'module1' id ├── nested-module2.js # 'nestedModule2' id ├── nested-module3.js # 'nestedModule3' id └── nested.js # 'nested' idAwait Pinia store actions in Nuxt pages
To prevent redundant data fetching during Server Side Rendering (SSR), call store actions inside thecallOnce()composable. This ensures the action is executed only once. You can control whether the action runs once on the client or on every navigation by setting themodeoption to'navigation'.<script setup> const store = useStore() // Execute once await callOnce('user', () => store.fetchUser()) // Execute on every navigation await callOnce('user', () => store.fetchUser(), { mode: 'navigation' }) </script>Use Pinia stores outside of Nuxt component setup
When using a store outside ofsetup()or injection-aware contexts (such as navigation guards, other stores, or Nuxt middleware), you must explicitly pass thepiniainstance to theuseStore()function to ensure the correct state is accessed.import { useStore } from '~/stores/myStore' // The pinia instance must be available in the current scope const store = useStore(pinia)Access getters from other stores
To use a getter from a different store, simply call the other store's hook inside the getter function.import { useOtherStore } from './other-store' export const useStore = defineStore('main', { state: () => ({ /* ... */ }), getters: { otherGetter(state) { const otherStore = useOtherStore() return state.localData + otherStore.data }, }, })Enable Hot Module Replacement (HMR) for Pinia stores
Pinia supports Hot Module Replacement (HMR), allowing developers to modify state, actions, and getters in a store without reloading the page, thereby preserving the current application state. While officially supported in Vite, it works with any bundler implementing theimport.meta.hotspecification. To enable HMR, you must callacceptHMRUpdatewithin animport.meta.hot.acceptblock immediately after the store definition.// auth.js import { defineStore, acceptHMRUpdate } from 'pinia' export const useAuth = defineStore('auth', { // store options... }) // Enable HMR by passing the store definition and the hot module object if (import.meta.hot) { import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot)) }Use Pinia stores in serverPrefetch and onServerPrefetch
In the Options APIserverPrefetch()hook, you can access the store usingthis.$pinia. In the Composition APIonServerPrefetch()hook, you can calluseStore()normally without passing the pinia instance.// Options API export default { serverPrefetch() { const store = useStore(this.$pinia) }, }<script setup> const store = useStore() onServerPrefetch(async () => { // This works automatically await store.fetchData() }) </script>Access other Pinia stores within an action
To use another store inside an action, simply call the other store's hook directly within the action method.import { defineStore } from 'pinia' import { useAuthStore } from './auth-store' export const useSettingsStore = defineStore('settings', { state: () => ({ preferences: null }), actions: { async fetchUserPreferences() { const auth = useAuthStore() if (auth.isAuthenticated) { this.preferences = await fetchPreferences() } else { throw new Error('User must be authenticated') } }, }, })Access and modify Pinia state
State can be read and written directly via the store instance. Because no verbose wrappers are required, state can be bound directly tov-model. Note that you cannot add new state properties that were not defined in the initialstate()function.const store = useStore() // Direct modification store.count++<!-- Direct binding in template --> <input v-model="store.count" type="number" />Use Pinia v3 with Nuxt
The Pinia Nuxt module is updated to support Nuxt 3. If you are using Nuxt 2 or Nuxt Bridge, you should continue using a previous version of Pinia.Hydrate Pinia state for SSR
To prevent state mismatch between server and client, the server'srootStatemust be serialized and embedded in the HTML. For security (XSS prevention), state should be escaped; thedevaluelibrary is recommended. On the client side, the state must be hydrated into the pinia instance before anyuseStore()calls are made.// Server-side: Serialize state import devalue from 'devalue' const pinia = createPinia() // ... after rendering page const serializedState = devalue(pinia.state.value) // Client-side: Hydrate state const pinia = createPinia() const app = createApp(App) app.use(pinia) if (isClient) { // Assuming state was placed in window.__pinia pinia.state.value = JSON.parse(window.__pinia) }