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).

Tokens
15.5K
Snippets
88
Records
99
Agent score
64%

What's inside Pinia

  1. Overview of Pinia Store Library

    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).
  2. 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.

  3. 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 of store/ 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' id
  4. Await Pinia store actions in Nuxt pages

    To prevent redundant data fetching during Server Side Rendering (SSR), call store actions inside the callOnce() 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 the mode option 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>
  5. Use Pinia stores outside of Nuxt component setup

    When using a store outside of setup() or injection-aware contexts (such as navigation guards, other stores, or Nuxt middleware), you must explicitly pass the pinia instance to the useStore() 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)
  6. 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
        },
      },
    })
    
  7. 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 the import.meta.hot specification. To enable HMR, you must call acceptHMRUpdate within an import.meta.hot.accept block 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))
    }
  8. Use Pinia stores in serverPrefetch and onServerPrefetch

    In the Options API serverPrefetch() hook, you can access the store using this.$pinia. In the Composition API onServerPrefetch() hook, you can call useStore() 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>
  9. 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')
          }
        },
      },
    })
    
  10. 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 to v-model. Note that you cannot add new state properties that were not defined in the initial state() function.
    const store = useStore()
    
    // Direct modification
    store.count++
    <!-- Direct binding in template -->
    <input v-model="store.count" type="number" />
  11. Hydrate Pinia state for SSR

    To prevent state mismatch between server and client, the server's rootState must be serialized and embedded in the HTML. For security (XSS prevention), state should be escaped; the devalue library is recommended. On the client side, the state must be hydrated into the pinia instance before any useStore() 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)
    }