Persist Pinia store state to localStorage, sessionStorage, or other storage mechanisms.
Manual Persistence
Simple localStorage Sync
typescriptimport { defineStore } from 'pinia' export const useSettingsStore = defineStore('settings', { state: () => { // Load from localStorage on initialization const stored = localStorage.getItem('settings') return stored ? JSON.parse(stored) : { theme: 'light', fontSize: 14, language: 'en' } }, actions: { setTheme(theme: string) { this.theme = theme this.save() }, save() { localStorage.setItem('settings', JSON.stringify(this.$state)) } } })
Using $subscribe
typescriptconst store = useSettingsStore() // Auto-save on any change store.$subscribe((mutation, state) => { localStorage.setItem('settings', JSON.stringify(state)) })
Persistence Plugin
Create a reusable persistence plugin:
typescriptimport { PiniaPluginContext, StateTree } from 'pinia' type PersistOptions = { key?: string storage?: Storage paths?: string[] } export function persistPlugin(context: PiniaPluginContext) { const { store, options } = context // Check if store has persist options const persist = (options as any).persist as PersistOptions | boolean if (!persist) return const opts: PersistOptions = typeof persist === 'boolean' ? {} : persist const key = opts.key ?? store.$id const storage = opts.storage ?? localStorage const paths = opts.paths // Restore state const stored = storage.getItem(key) if (stored) { try { const parsed = JSON.parse(stored) store.$patch(parsed) } catch (e) { console.error('Failed to restore state:', e) } } // Save on changes store.$subscribe((_, state) => { const toStore = paths ? paths.reduce((acc, path) => { const value = path.split('.').reduce((obj, key) => obj?.[key], state as any) if (value !== undefined) { acc[path] = value } return acc }, {} as Record<string, any>) : state storage.setItem(key, JSON.stringify(toStore)) }) } // Usage export const useUserStore = defineStore('user', { state: () => ({ name: '', email: '', token: '', // Sensitive - maybe don't persist preferences: {} }), persist: { key: 'user-data', storage: localStorage, paths: ['name', 'email', 'preferences'] // Only persist these } })
pinia-plugin-persistedstate
The recommended solution for persistence:
bashnpm install pinia-plugin-persistedstate
typescript// main.ts import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(piniaPluginPersistedstate)
typescript// store export const useUserStore = defineStore('user', { state: () => ({ name: '', email: '', token: '' }), persist: true // Simple: persist everything }) // Advanced options export const useCartStore = defineStore('cart', { state: () => ({ items: [], lastUpdated: null }), persist: { key: 'shopping-cart', storage: sessionStorage, // Use sessionStorage paths: ['items'], // Only persist items beforeRestore: (ctx) => { console.log('About to restore', ctx.store.$id) }, afterRestore: (ctx) => { console.log('Restored', ctx.store.$id) } } })
Setup Syntax with Persistence
typescriptimport { ref } from 'vue' import { defineStore } from 'pinia' export const useAuthStore = defineStore('auth', () => { const token = ref('') const user = ref(null) function setToken(newToken: string) { token.value = newToken } function logout() { token.value = '' user.value = null } return { token, user, setToken, logout } }, { persist: { paths: ['token'] } })
Custom Serialization
typescriptexport const useStore = defineStore('store', { state: () => ({ date: new Date(), map: new Map() }), persist: { serializer: { serialize: (state) => JSON.stringify({ ...state, date: state.date.toISOString(), map: Array.from(state.map.entries()) }), deserialize: (str) => { const parsed = JSON.parse(str) return { ...parsed, date: new Date(parsed.date), map: new Map(parsed.map) } } } } })