Make Your Vue App Last with Local Storage

Keep your user’s data available with or without a connection using Vue and LocalStorage

Photo by rawpixel on Unsplash

No Internet, No Problem

The web is undoubtedly an amazing platform enabling people to communicate, share, and experience things in insane ways that weren’t possible until recently. As web-developers, we have the honor of creating such experiences.

The internet is great but only if you can access it. When you can’t, user’s shouldn’t be permanently deprived of it’s services. One way to solve this is by using Client-Side storage.

Client-Side storage enables users to access their data even when offline, it also serves the double purpose of speeding up interactions. With data stored locally, your site or app might not even need to send a server request and wait for a response.

A faster and more practical internet is our goal so let’s look at one way to get there.

What we will do

In a previous article we created a ToDo list app, it worked as expected but reloading the page would remove all the tasks we had set. We’re going to look at one method of storing this information on the browser.

Client-Side Options

There are a few ways to implement client-side storage each with it’s pros and cons.


LocalStorage is one of the storage mechanisms in the Web Storage API that allows browsers to store information using key-value pairs. It is best suited for simple data values because it can only store strings. If you have to use LocalStorage for more complicated data you will have to parse it into JSON and back.


If you want to store more complex data, files, or even blobs in the browser IndexedDB is what you’re looking for. It is also the database you would need when making a PWA.

IndexedDB is a transaction based database system that is described by MDN as a JavaScript-based object-oriented database. What this means is that data isn’t stored in table with rows and columns as with SQL-based RDBMS but rather with JavaScript objects that are accessible/indexed with a key.

Read more at MDN

I’ve also written another article where we implement IndexedDB.

For our ToDo app LocalStorage is more than enough so lets implement it.


If you want to follow along git clone the proper todo list branch to get started.

$ git clone -b formReuse --single-branch

The final project is found at:

$ git clone -b localStorage --single-branch

Adding LocalStorage

Actually adding localStorage to our todo app is incredibly simple and works in two steps.

The first step is to check localStorage for already exisiting data with which to fill our component (aka Read), the second step is to save the state of our todos into localStorage (aka Write).

To do this, we will use two of the methods offered by the localStorage API, getItem and setItem.

Reading from Storage

In order load data into the component at the right moment, we will hook into the component lifecycle using mounted.

There, we check if localStorage has values associated with the ‘todos’ and ‘completed’ keys, it makes sense to use the key names as we do in our component data. We can actually take it a step further and have an extra value called dataFields which would hold the name of any future values we would like to store on the client.

In our checkStorage method we assume that any error means the data is corrupted and we can then simply remove it.

Another thing to keep in mind is that localStorage only accepts string values, for arrays and objects, we need to parse the data into JSON and back.

Writing to Storage

Saving our todos is just as simple.

We will create a saveTodos method that will go through our useful dataFields array, stringify the data and store it.

What about edits?

To be aware that a todo has been edited we also need to emit an event from the ListItem component and listen to it in TodoList.

With that, all we need to do is call the method whenever we add, delete, edit, or complete a todo!

Taking things further

Now our todo app has persistent data, you can try it out by adding some tasks and reloading the page, you’ll see that they’re still present.

Making it work is only the start. There are actually a few extra things we could do to help make our app more maintainable, they might not make sense for a simple app such as this but in larger programs it is critical.

One thing to do is to decouple our obvious dependency on localStorage. Currently all the storage related code is right inside the component, if we ever want to switch to using IndexedDB it would require quite a bit of rewriting, it gets even worse if you also want to add calls to a server where more data is stored.

Refactor refactor refactor

The first thing we will do is add Vuex to our app. It isn’t absolutely necessary but it does set us up for whatever future changes we might go through. Vuex will help us separate concerns even more by ensuring components only deal with the data and actions that directly concern them.

Add vuex to the app with

$ vue add vuex

Next we move the state and every method changing it from our component to the store.

The way I usually work with Vuex is to only dispatch actions from components. In the store these actions will then manage calls to mutations or other actions. To make this as practical as possible, I try to make my mutations simple and atomic so it often happens that an action will call a mutation multiple times with different arguments. This works well since mutations are meant to only directly change the state and finish rather fast.

Of course this is just how I personally use it, with an application as simple as ours you can skip using actions all together.

Abstract abstract abstract

The next step is to reduce our dependence on LocalStorage, or at least make it as painless as possible to change or even switch away from it.

To do that we’ll create an api/ folder with a localStorageService.js file. There we place all calls to localStorage from the store.

The advantage of this is whenever we want to change how localStorage behaves we can just make changes there, even better, if we decide to use something else, the other option only needs to export functions with the same signature.

You can get the code for that from this branch:

$ git clone -b vuexRefactor--single-branch


We’ve updated our todo app to now keep it’s state saved even if the page reloads or if the user leaves and returns to it later. This also has the added benefit of not needing to make calls to a server each time the page loads.

Even more than that, we started future-proofing the app for future updates, our client-side storage option is super modular and components are nicely compartmentalized.

Hopefully this helped you out in your own projects.