Unlock the Power of Pinia LocalStorage with Pinia Persist

Posted September 9, 2023
Unlock the Power of Pinia LocalStorage for Pinia Persist

Pinia lacks Persisted states. Dive in, use Pinia Persist to LocalStorage in this Pinia LocalStorage example and shine Pinia with persistent

Pinia manages the states of the Vue.js app. However, it doesn’t persist data saved across the states. Now, enter Pinia LocalStorage integration. This dynamic duo ensures your state remains intact even after the page refreshes.

In this guide, you will learn an elegant way of using Pinia LocalStorage and say goodbye to losing any Pinia store throughout page reloads.

Prerequisites

Before delving into this hands-on guide, ensure you have set up a Vue app with Pinia. If Not, Check my Comprehensive Guide to Vue.js State Management using Pinia. If you love TypeScript, This Pinia store and typescript guide has you covered.

Pinia Persist and LocalStorage Duo

State management doesn’t have to be complicated, and Pinia is perfect for making your Vue.js state management smooth.

To explore the magic pineapple (I mean Pinia 😉) possibilities 🍍, It may require you to persist in its states, and Pinia doesn’t have persistence support. Pinia can sore its states inside the LocalStorage of your browser with minor twerks.

Example of Non-persistent Pinia Store

The following example used Pinia to create a TodoStore:

import { defineStore } from "pinia";

export const useTodoStore = defineStore("todo", {
  state: () => ({
    todos: [],
  }),
  actions: {
    add() {
      if (!this.todoMessage) return (this.error = true);
      const todoData = {
        id: Math.floor(Math.random() * 1000),
        title: this.todoMessage,
        complete: false,
      };
      this.todos.push(todoData);
    },
    update(id, status) {
      const index = this.todos.findIndex((item) => item.id === id);
      this.todos[index] = { ...this.todos[index], complete: status };
    },
    remove(id) {
      this.todos = this.todos.filter((item) => item.id !== id);
    },
  },
  getters: {
    getTodos: (state) => state.todos,
    hasError: (state) => state.error,
  },
});

Now, this store creates the following basic todo app (I have hosted the full code on GitHub):

Unlock the Power of Pinia LocalStorage for Pinia Persist

If you have created such an app using Pinia, the added states will be lost once you hit a page reload. Meaning the states still need to be persisted.

Now, let’s solve this problem using Pinia LocalStorage.

Persisting Pinia inside LocalStorage

Local storage features the client’s browser to store web app data. You can use this approach and persist Pinia states. This way, Local storage will always state persistence even on page refreshes or after the browser is closed.

Let’s see Pinia and LocalStorage in action. Pinia uses:

  • Key property to read the object of the state:
  state: () => ({
    todos: [],
  }),
  • actions to create mutations that update the state.

These two represent the lifecycle of the Pinia state and how it changes the state data. To persist pinia, they will be the main focus the LocalStorage needs access to.

At the same time, LocalStorage uses:

  • localStorage.setItem(key, value) to store data in the local storage space of a browser. This method is closely related to actions in Pinia
  • localStorage.getItem(key) to read the data you have saved in the browser. This scenario repsent object todos: [] to the state property.

With this understanding, you can take your Pinia sore and add localStorage in the following steps.

Step One: Creating Pinia localStorage action

The first step to persisting pinia is allowing localStorage access to the states that pinia saves using a Pinia action.

Inside your actions create a saveTodos method so localStorage.setItem stores the todos array in the local storage. Your saveTodos will be as follows:

saveTodos() {
  localStorage.setItem("todos", JSON.stringify(this.todos));
},

Considering localStorage.setItem(key, value), todos is the key under which your todos data will be stored in local storage, taking the value of this.todos containing the todo items.

You must understand that localStorage will always store data as strings. Your Pinia store saves todo items todos: [] in an array. JSON.stringify() will take care of converting your this.todos array to a string that localStorage can understand.

Step Two: Persisting Pinia actions to localStorage

In the useTodoStore Pinia store, you have the following actions (once you have added saveTodos()):

  actions: {
    saveTodos() {
      localStorage.setItem("todos", JSON.stringify(this.todos));
    },

    add() {
      if (!this.todoMessage) return (this.error = true);
      const todoData = {
        id: Math.floor(Math.random() * 1000),
        title: this.todoMessage,
        complete: false,
      };
      this.todos.push(todoData);
    },
    update(id, status) {
      const index = this.todos.findIndex((item) => item.id === id);
      this.todos[index] = {
        ...this.todos[index],
        complete: status,
      };
    },

    remove(id) {
      this.todos = this.todos.filter((item) => item.id !== id);
    },
  },

In your example, every time remove(id), update(id, status), and add() are called, the state will, of course, get the update. These calls should be captured and persisted, so if a new todo is added, calling add() localStorage will store it.

Here is what you must do to persist in these actions. Just call the saveTodos method in each action and save the most current state as follows:

  actions: {
    saveTodos() {
      localStorage.setItem("todos", JSON.stringify(this.todos));
    },

    add() {
      if (!this.todoMessage) return (this.error = true);
      const todoData = {
        id: Math.floor(Math.random() * 1000),
        title: this.todoMessage,
        complete: false,
      };
      this.todos.push(todoData);
      this.saveTodos();
    },
    update(id, status) {
      const index = this.todos.findIndex((item) => item.id === id);
      this.todos[index] = {
        ...this.todos[index],
        complete: status,
      };
      this.saveTodos();
    },

    remove(id) {
      this.todos = this.todos.filter((item) => item.id !== id);
      this.saveTodos();
    },
  },

Rerun your Pinia state app. This time, add change to the actions method will be stored by localStorage. To verify such, go to your browser inspect, and under Application, check your localStorage as follows:

Unlock the Power of Pinia LocalStorage for Pinia Persist

Unlock the Power of Pinia LocalStorage for Pinia Persist

Hit your page reload, and see if Pinia will load your saved localStorage data.

Unlock the Power of Pinia LocalStorage for Pinia Persist

On page refresh, you will realize that the localStorage persisted in your pinia state. However, Pinia can’t get to display the persisted localStorage. And you have to add the final check.

Step Three: Fetching Pinia persistent localStorage data

Remember, localStorage uses localStorage.getItem(key) to read the data you persited with Pinia in the browser. This scenario repsent object todos: [] to the state property.

In this step, todos: [] must point to localStorage.getItem) to read and display the localStorage data to any page reload.

  state: () => ({
    todos: [],
  }),

Will be replaced as such:

  state: () => ({
    todos: JSON.parse(localStorage.getItem("todos")) || [],
  }),

It’s pretty simple here. Pinia state property and its todos object will read the todos on the browser. And because states return an array of the items, JSON.parse will read the JSON Localstorage into the array todos: [].

Conclusion

Any time you reload your page states, you will have persistent pinia inside the Local storage.

Now that you have learned how to create persistent Pinia with Local storage, why now expand your newfound knowledge and unlock the door to a new level of Pinia state management with the following topics:

Unlock the Power of Pinia LocalStorage with Pinia Persist

Written By:

Joseph Chege