Skip to content

Persisting State

By default, Pinia state is in-memory — it resets when the page is refreshed. For FamilyTree, that would mean losing the entire family tree on every reload. pinia-plugin-persistedstate solves this by serializing store state to localStorage automatically.

Terminal window
npm install pinia-plugin-persistedstate

Register it in main.ts:

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
import router from './router'
const app = createApp(App)
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
app.use(router)
app.mount('#app')

Add persist: true as the third argument to defineStore:

export const useFamilyStore = defineStore(
'family',
() => {
const people = ref<Person[]>([])
// ...
return { people, /* ... */ }
},
{ persist: true }
)

That’s it. On every state change, Pinia serializes the returned state to localStorage under the key 'family'. On page load, it restores the state before any component mounts.

When persisted data is loaded from localStorage, the plugin’s afterHydrate callback runs. This is the right place to normalize data — for example, if you added a new field to your Person type after users already had data saved.

Without normalization, old Person objects in localStorage won’t have the new field, causing errors when code expects it to exist.

export const useFamilyStore = defineStore(
'family',
() => {
const people = ref<Person[]>([])
// ...
return { people }
},
{
persist: {
afterHydrate: (ctx) => {
// Ensure all Person objects have required array fields
// (handles data saved before these fields were added)
ctx.store.$patch({
people: (ctx.store.people as any[]).map((p: any) => ({
parentIds: [],
stepParentIds: [],
spouses: [],
siblingIds: [],
...p, // existing values override defaults
})),
})
},
},
}
)

This pattern saved FamilyTree when stepParentIds and spouses were added — users who had data saved without those fields got them auto-normalized instead of seeing a TypeError.

By default, everything returned from the store setup is persisted. You can configure pick or omit to select specific keys:

{ persist: { pick: ['people', 'focusPersonId'] } }
  1. Install pinia-plugin-persistedstate and register it in main.ts.
  2. Add { persist: true } to your counter store.
  3. Increment the counter a few times, then refresh the page. Confirm the count survives.
  4. Open DevTools → Application → localStorage to see the serialized data.
  • pinia-plugin-persistedstate auto-serializes store state to localStorage.
  • Register with pinia.use(piniaPluginPersistedstate) and add { persist: true } to the store.
  • afterHydrate runs after state is restored — use it to normalize stale persisted data.
  • Use pick or omit to control which state keys are persisted.