Vue cookbook · Beginner

Persist a Pinia store to localStorage

The cart or preferences disappear when users reload the page.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Load the initial state from storage in the setup store, save changes with a deep watcher, and guard against invalid stored data and server-side rendering (no localStorage on the server).

Solution

// stores/preferences.ts
import { defineStore } from "pinia";
import { ref, watch } from "vue";

type Preferences = { theme: "light" | "dark" | "system"; currency: string; recentSearches: string[] };
const KEY = "preferences:v1";
const defaults: Preferences = { theme: "system", currency: "USD", recentSearches: [] };

function load(): Preferences {
  if (typeof window === "undefined") return { ...defaults };
  try {
    return { ...defaults, ...JSON.parse(localStorage.getItem(KEY) ?? "{}") };
  } catch {
    return { ...defaults };                               // corrupted or old data
  }
}

export const usePreferences = defineStore("preferences", () => {
  const prefs = ref<Preferences>(load());

  watch(prefs, (value) => {
    if (typeof window !== "undefined") localStorage.setItem(KEY, JSON.stringify(value));
  }, { deep: true });

  function addSearch(term: string) {
    prefs.value.recentSearches = [term, ...prefs.value.recentSearches.filter((t) => t !== term)].slice(0, 5);
  }

  return { prefs, addSearch };
});

Pitfalls

  • Storing sensitive data (tokens, personal data) in localStorage, readable by any script on the page.
  • No version in the key, so changed shapes break old stored data.
  • Reading localStorage during SSR.

Variations

  • pinia-plugin-persistedstate adds a persist: true option to stores.
  • Sync between tabs with the storage event.

Further reading: Pinia

Reference: https://pinia.vuejs.org/core-concepts/

More Vue recipes

Was this page helpful?