A Ref That Saves Itself

Saving in localStorage always takes the same lines: read what's saved when the ref is created, and save it with watch whenever it changes. With a composable, you write them only once:

import { ref, watch } from "vue";

export function useLocalStorage(key, initial) {
    const saved = localStorage.getItem(key);
    const value = ref(saved === null ? initial : JSON.parse(saved));

    watch(
        value,
        (newValue) => {
            localStorage.setItem(key, JSON.stringify(newValue));
        },
        { deep: true },
    );

    return value;
}

Then each value that should stay takes one line:

const name = useLocalStorage("name", "");
const order = useLocalStorage("order", []);

How it works:

  • The composable has two parameters: key, the name under which it saves, and initial, the value to start with when nothing has been saved yet. Thanks to them, the same function saves a name under "name" and an order under "order".
  • It returns a single ref, not an object, so there's nothing to take apart: after const name = useLocalStorage("name", "");, name is a ref, ready for v-model or {{ name }}.
  • The watch inside belongs to the call: each call watches its own ref and saves it under its own key. { deep: true } is there so that a push() into an array is saved too, as you know from Computed Values and Watchers.
  • JSON.stringify() and JSON.parse() work for text, numbers, true and false too, so the composable can save anything.

The component doesn't see any of it. It works with name and order as with any other ref, and they save themselves.

Instructions

  1. In useLocalStorage.js, write useLocalStorage(key, initial) as shown above. In App.vue, import it, and use it for the name: const name = useLocalStorage("name", "");. Try it: type your name and click Run. The name stays.

  2. Use it for the order too, with the key order and an empty array to start with. Order something and click Run: the order stays. And Clear the order empties it for good.