is coming soon...
ado.xyz

Master the Web Storage API

February 10, 2019

The Web Storage API is a JavaScript based API that allows modern browsers to store and persist data in the users browser. The Web Storage API exposes two mechanisms for this called localStorage and sessionStorage. These APIs are easy to learn and master, but can easily be abused. This article will teach you all about the Web Storage API, why it exists, how to use it in your applications, and most importantly when to use it and when to avoid using it.

Web Storage History

The concept of Web Storage goes back only about 10 years. The initial working draft by the W3C was published on April 23, 2009 by Ian Hickson . The Web Storage proposal called for browsers to gain the capability of storing and persisting data on the client side within the browser. Up until this point, the only mechanism available for persisting data in the browser was cookies which had many limitations as well as security concerns.

Cookies had both a size and capacity limitation. Most browsers, even today, limit the number of cookies a domain can hold. These limits range from 50 to 600, although some browsers support an unlimited amount of cookies. The bigger limitation is size. Most browsers enforce a max cookie size of 4096 bytes or 4kb. Cookies were never meant to be a data store for the browser, so this limitation makes sense.

On the security considerations side, cookies are automatically sent to the backend every time an HTTP request is made. This means that any data stored in the cookie could be compromised in transit via a man-in-the-middle attack. There are a number of other considerations that make cookies a bad choice for persistent data storage on the browser client that are outside of the scope of this article.

The Web Storage or DOM Storage proposal aimed to address the issue of persistent data storage on the client side, i.e. the browser. The proposal called for two unique capabilities called localStorage and sessionStorage.

The first, localStorage, would allow the browser to store and persist data that can outlive a single user session. The second, sessionStorage, was much more restrictive allowing data to only exist during a users active session, so once they closed their browser, whatever was in sessionStorage would be deleted. The data in sessionStorage would also be independent in different tabs and windows as well, which is not possible with cookies.

Data storage limitations for the Web Storage API would be much larger than for cookies. Many browsers allow up to 10MB of data or more vs the 4kb limit of cookies, which represents a 1000x increase in storage capacity. Unlike cookies, data stored via the Web Storage API would only be available client side and not automatically sent to the backend.

When all was said and done the Web Storage proposal was accepted and many vendors quickly implemented it in their browsers. The simplicity of the API made it easy to learn, but gave web developers a lot of power and control when it came to storing data client-side.

Browser Compatibility

The Web Storage API proposal was accepted by virtually all browser vendors, including Internet Explorer. Today, you’ll be hard pressed to find a browser that does not support the Web Storage API.

The one exception to note is that if you are using Private Browsing in Safari. Even though localStorage and sessionStorage is available, if you try to store data you will get an exception that says that the capacity of the store is 0. According to the spec, this is acceptable as private browsing and persistent data can be seen as incompatible with each other. This may change in the future for Safari, where at least the sessionStorage store is available.

Using the Web Storage API

Both localStorage and sessionStorage use essentially the same API, which lives on the window object and exposes just a handful of methods to get the job done. Data stored in either can only be stored as strings. Additionally, data is stored as key-value pairs. What this means is that for each item in your store, you will give it an identifier and then the data you wish to store. We’ll see how that works in just a moment.

The Web Storage API is a browser feature and thus lives on the global namespace. This means that we can access localStorage and sessionStorage directly or alternatively by using window.localStorage or window.sessionStorage. Either way is perfectly acceptable, and in this article for brevity I will omit window. The only time you would be required to use window is if you had a local variable called localStorage or sessionStorage that did something other than what the Web Storage API intends, but I would highly recommend against that.

Ensuring Storage is Available

Before we attempt to write data to our storage, we’ll want to ensure that our browser has the capability to work with the Web Storage API. Like I mentioned in the browser compatibility section, most modern browsers support the Web Storage API, so you likely don’t need to do this, but for completeness I’ll show you how to check. There are a number of different ways we can ensure that we have access to localStorage or sessionStorage. Let’s look at some examples and explanations below.

MDN Recommendation

The Mozilla Developer Network has an excellent recommendation for ensuring that localStorage or sessionStorage is available. I really like their approach as it will also give you a reason for why you might not be able to use the Web Storage APIs. Their recommendation is:

function storageAvailable(type) {
  try {
    var storage = window[type],
      x = '__storage_test__'
    storage.setItem(x, x)
    storage.removeItem(x)
    return true
  } catch (e) {
    return (
      e instanceof DOMException &&
      // everything except Firefox
      (e.code === 22 ||
        // Firefox
        e.code === 1014 ||
        // test name field too, because code might not be present
        // everything except Firefox
        e.name === 'QuotaExceededError' ||
        // Firefox
        e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
      // acknowledge QuotaExceededError only if there's something already stored
      storage.length !== 0
    )
  }
}

if (storageAvailable('localStorage')) {
  // Yippee! We can use localStorage awesomeness
} else {
  // Too bad, no localStorage for us
}

if (storageAvailable('sessionStorage')) {
  // Yipee! We can use sessionStorage awesomeness
} else {
  // Too bad, no sessionStorage for us
}

I think that Mozilla’s recommendation is the one to follow. The code is fairly simple, but most importantly it handles the various exception codes you may receive, which is a huge bonus.

Other Options

You may want something simpler that just tells you that localStorage or sessionStorage is available or unavailable. Here’s a few different ways to do it:

// Will return true if localStorage is available
!!window.localStorage

// Will return true if sessionStorage is available
!!('sessionStorage' in window)

// If localStorage is available will set a key-value pair of test:test, remove it, and return true
// If localStorage is not available will return false indicating that it is not available
try {
  localStorage.setItem('test', 'test')
  localStorage.removeItem('test')
  return true
} catch (e) {
  return false
}

My recommendation still would be to use the Mozilla way of checking for localStorage or sessionStorage as it will give you an explanation of why the storage option you are trying to access is unavailable.

Storing Data in Local Storage

Once we know that our store is available, we’re free to write and persist data. There are a number of different ways to accomplish this. The Web Storage API exposes the setItem() method which is the recommended way of storing data, but there are two other ways to do it.

// Using the setItem() method *Recommended*
localStorage.setItem('message', "I'm going to hang out in localStorage")

// Setting a new property on the localStorage object
localStorage.message2 = "I'm also going to hang out in localStorage"

// Setting a new key on the localStorage object
localStorage['message3'] = 'Hey make room, I want in on this localStorage party'

While each of the three ways is perfectly acceptable, my recommendation would be to stick with setItem().

Data stored via the Web Storage API is always stored as a string. That does not mean that we can only store strings in localStorage, it just means that we’ll have to do a bit of conversion when it comes to storing and retrieving our data.

Let’s first look at a simple example: storing a number. To store a number in localStorage we can do the following:

// The browser will automatically convert the number 29 to a string
localStorage.setItem('age', 29)

// Explicitley convert the number 29 to a string and then store in localStorage
localStorage.setItem('age', String(29))

// Convert the age variable to a string
let age = 29
localStorage.setItem('age', age.toString())

The vast majority of browsers will automatically handle converting your number into a string before storing it in localStorage, but to be sure I would always convert it myself.

Next, let’s take a look at how to store a JSON object in localStorage.

let human = {
  name: 'Ado',
  age: 29,
  skills: ['JavaScript', 'Go', 'Node'],
  location: {
    city: 'Las Vegas',
    state: 'Nevada',
  },
}

// If you try to do this the user key will have the value of [object Object]
localStorage.setItem('user', human)

// Using JSON.stringify we convert our JSON data to a string
// Now we can store it properly in localStorage
localStorage.setItem('user', JSON.stringify(human))

Finally, what if you wanted to store an array of data in localStorage?

// Create an array of sports
let activities = ['Football', 'Soccer', 'Hockey']

// This will work, but will create a string with each item comma-delimited
localStorage.setItem('sports', activities)
// Store as string: "Football, Soccer, Hockey"

// Better way, like in the JSON object example will store data and keep structure
localStorage.setItem('sports', JSON.stringify(activities)) //
// Store as string: "["Football", "Soccer", "Hockey"]"

Storing complex data in localStorage is possible. The one consideration to keep in mind is that you will need to do some extra work to convert that data back to a number, array, or object. Luckily, JavaScript has a lot of handy methods to help with this like JSON.parse to convert a string to JSON object or parseInt() to convert a string containing a number to a number and we’ll see examples of that in the next section.

Retrieving Data from Local Storage

The entire point of storing data is to be able to retrieve it and in this section we are going to look at methods on the localStorage object that will allow us to do just that. Just like we can store data multiple ways, we have those same capabilities when it comes to retrieving data from localStorage. My recommendation here is to use the getItem() method so that your code can remain consistent.

// Using the getItem() method *Recommended*
localStorage.getItem('message')

// Accessing the property directly from localStorage
localStorage.message2

// Accessing the data by key from localStorage
localStorage['message3']

If all of our data were just strings, this section could be over, but since we are likely to be working with various data types, I want to cover how to convert more complex data from localStorage.

First up, let’s see how we can work with numbers.

// Retrieve the value for the key age and store it to a variable called age
let age = localStorage.getItem('age') // "29"

The age variable at this point is a string "29" so we cannot do any number comparisons, use the value as an iterator, and so on. What we’re going to need to do is convert that string value to a number.

// Get our value from localStorage
let age = localStorage.getItem('age') // "29"

// Convert it to a number
age = parseInt(age) // 29

We can also use the parseFloat() and Number() methods to convert a string to a number value. Pretty easy right?

Next, let’s take a look at converting a JSON object from a string to a usable JavaScript object. One key point to remember here is that if the object was saved directly without first converting it to a string what you’ll get back is [object Object] in which case you can’t do anything with it.

let human = localStorage.getItem('human') // "{name:"ado", age: 29}"

By default, the human variable is a string "{name:"ado", age: 29}". This means that we cannot access the properties name or age, and if we try, we’ll just get an undefined error. To convert this string back to a JSON object, we’ll do the following:

let human = localStorage.getItem('human') // "{name:"ado", age: 29}"

human = JSON.parse(human)

Now our human variable is a JavaScript option and we can access the properties on it. Also, the age property will be a number so you don’t have to do any additional conversions.

Finally, let’s look at arrays. Depending on how we stored our array in localStorage will determine the method we use to turn the array string back into an actual array object.

Let’s first assume we stored it directly like localStorage.setItem("sports", sports).

let sports = localStorage.getItem('sports') // "football, soccer, hockey"

At this point our sports variable is a string "football, soccer, hockey". Since it is not an array we can’t access "football" for example by doing sports[0]. To fix that we’ll do:

let sports = localStorage.getItem('sports')

sports = sports.split(',')

Next, let’s take a look at the case in which we stringified our array before storing it in localStorage like this: localStorage.setItem("sports", JSON.stringify(sports)).

let sports = localStorage.getItem('sports') // "["football", "soccer", "hockey"]"

Again, by default, our sports variable is a string, but this time looks like ["football", "soccer", "hockey"]. Since it is not an array we still can’t access "football" by doing sports[0]. To fix that we’ll do:

let sports = localStorage.getItem('sports')

sports = JSON.parse(sports)

Now our sports variable is an array and we can access individual items by their index like sports[0] which would return "football".

Updating Data in Local Storage

You may want to update data that is stored in your store. I have both good news and bad news. The bad news is that there is no way update data stored without overwriting it. This means that once you overwrite the data that is attributed to a specific key, there is no way to retrieve the original value. The good news is that overwriting the data is as simple as storing the data in the first place.

// Set data in localStorage
localStorage.setItem('message', 'You are great')

// Retrieve data from localStorage
localStorage.getItem('message') // "You are great"

// Overwrite the message key in localStorage
localStorage.setItem('message', 'You are amazing')

// Retrieve data from localStorage
localStorage.getItem('message') // "You are amazing"

As you can see, overwriting data in localStorage is easy. You will not be alerted that you are about to overwrite data, although you could write your own logic for that. Once data has been overwritten it cannot be retrieved.

Deleting Data from Local Storage

Data stored in localStorage will not be automatically deleted. There are only a number of cases where the contents will be purged such as if a user resets all of their browser settings and deletes history or manually goes into your domains localStorage via developer tools and deletes the contents. Likewise, you may wish to occasionally remove key-value pairs that are no longer in use for your application. The Web Storage API exposes the removeItem() method to do just that. The removeItem() method deletes the key and any accompanying data that is attached to it.

localStorage.removeItem('message')

Using the removeItem() method will remove the key and accompanying data if it exists. If the key does not exist, you will not get an error. Once a key-value pair has been removed using the removeItem() method, it is gone forever and the contents of that specific key cannot be retrieved. You can, of course, set new data to that key using the Web Storage API.

Deleting All Data from Local Storage

You may come across a situation in which you wish to delete all of the key-value pairs in your localStorage store. Luckily, there is a method called clear() that will do just that. To delete all items currently in your localStorage store simply call the clear() method on your localStorage or window.localStorage object.

localStorage.clear()

Calling the clear() method will delete everything that is currently stored in localStorage. Be 100% certain that this is what you want to do, as there is no undo method that will return the data, you’ll have to add it back in yourself.

Local Storage Use Considerations

The localStorage API is more powerful than sessionStorage because of the data persistence across sessions. This can also be a drawback as well. Here are some things consider when deciding what to put in localStorage:

  • Do not store any sensitive data in localStorage.
  • Any JavaScript code on your page has full and unrestricted access to localStorage.
  • The data in localStorage is meant to be consumed client-side, for example, managing state or figuring out where to redirect.
  • While many browsers support 10MB of storage or more, Firefox limits localStorage to 5MB, so for compatibility you should limit the amount of data you put in localStorage to 5MB.
  • The Web Storage API is synchronous. This could affect performance if you have a lot of calls back and forth to localStorage.

Storing Data in Session Storage

The Web Storage API for sessionStorage is exactly the same as it is for localStorage. This means that the setItem() method is the recommended way of storing data in sessionStorage as well, but there are two other ways to do it.

// Using the setItem() method *Recommended*
sessionStorage.setItem('message', "I'm going to hang out in sessionStorage")

// Setting a new property on the sessionStorage object
sessionStorage.message2 = "I'm also going to hang out in sessionStorage"

// Setting a new key on the sessionStorage object
sessionStorage['message3'] =
  'Hey make room, I want in on this sessionStorage party'

The key point and difference to remember is that whatever value you store in sessionStorage is only available in the specific browser window or tab and will automatically be deleted once the user closes the tab or browser window. Additionally, even if two tabs of the same application are open, you cannot share data between those two sessions if they are stored in sessionStorage.

Storing non-string data in sessionStorage is the same as localStorage as well. I’ll provide code examples for completeness.

To store a number in sessionStorage we can do the following:

// The browser will automatically convert the number 29 to a string
sessionStorage.setItem('age', 29)

// Explicitly convert the number 29 to a string and then store in sessionStorage
sessionStorage.setItem('age', String(29))

// Convert the age variable to a string
let age = 29
sessionStorage.setItem('age', age.toString())

Next, let’s take a look at how to store a JSON object in sessionStorage.

let human = {
  name: 'Ado',
  age: 29,
  skills: ['JavaScript', 'Go', 'Node'],
  location: {
    city: 'Las Vegas',
    state: 'Nevada',
  },
}

// If you try to do this the user key will have the value of [object Object]
sessionStorage.setItem('user', human)

// Using JSON.stringify we convert our JSON data to a string
// Now we can store it properly in sessionStorage
sessionStorage.setItem('user', JSON.stringify(human))

Finally, storing an array in sessionStorage.

// Create an array of sports
let activities = ['Football', 'Soccer', 'Hockey']

// This will work, but will create a string with each item comma-delimited
sessionStorage.setItem('sports', activities)
// Store as string: "Football, Soccer, Hockey"

// Better way, like in the JSON object example will store data and keep structure
sessionStorage.setItem('sports', JSON.stringify(activities)) //
// Store as string: "["Football", "Soccer", "Hockey"]"

Storing complex data in sessionStorage is possible but is constrained to the same caveats as localStorage which means we’ll have to convert those non-string data structures ourselves.

Retrieving Data from Session Storage

Retrieving data in sessionStorage follows the same principles as localStorage.

// Using the getItem() method *Recommended*
sessionStorage.getItem('message')

// Accessing the property directly from sessionStorage
sessionStorage.message2

// Accessing the data by key from sessionStorage
sessionStorage['message3']

The non-string data structures also follow the same principles as localStorage. First up, let’s see how we can work with numbers.

// Retrieve the value for the key age and store it to a variable called age
let age = sessionStorage.getItem('age') // "29"

The age variable at this point is a string "29" so we cannot do any number comparisons, use the value as an iterator, and so on. What we’re going to need to do is convert that string value to a number.

// Get our value from localStorage
let age = sessionStorage.getItem('age') // "29"

// Convert it to a number
age = parseInt(age) // 29

Next up, converting a string to a JavaScript object. One key point to remember, that I think is worth reiterating, is that if the object was saved directly without first converting it to a string what you’ll get back is [object Object] in which case you can’t do anything with it.

let human = sessionStorage.getItem('human') // "{name:"ado", age: 29}"

By default, the human variable is a string "{name:"ado", age: 29}". This means that we cannot access the properties name or age, and if we try, we’ll just get an undefined error. To convert this string back to a JSON object, we’ll do the following:

let human = sessionStorage.getItem('human') // "{name:"ado", age: 29}"

human = JSON.parse(human)

Now our human variable is a JavaScript option and we can access the properties on it. Also, the age property will be a number so you don’t have to do any additional conversions.

Finally, let’s look at arrays and sessionStorage.

Let’s first assume we stored it directly like sessionStorage.setItem("sports", sports).

let sports = sessionStorage.getItem('sports') // "football, soccer, hockey"

At this point our sports variable is a string "football, soccer, hockey". Since it is not an array we can’t access "football" for example by doing sports[0]. To fix that we’ll do:

let sports = sessionStorage.getItem('sports')

sports = sports.split(',')

Next, let’s take a look at the case in which we stringified our array before storing it in sessionStorage like this: sessionStorage.setItem("sports", JSON.stringify(sports)).

let sports = sessionStorage.getItem('sports') // "["football", "soccer", "hockey"]"

Again, by default, our sports variable is a string, but this time looks like ["football", "soccer", "hockey"]. Since it is not an array we still can’t access "football" by doing sports[0]. To fix that we’ll do:

let sports = sessionStorage.getItem('sports')

sports = JSON.parse(sports)

Now our sports variable is an array and we can access individual items by their index like sports[0] which would return "football".

Updating Data in Session Storage

Updating data in sessionStorage follows the same principles as localStorage. Data that is overwritten cannot be retrieved and is lost forever.

// Set data in sessionStorage
sessionStorage.setItem('message', 'You are great')

// Retrieve data from sessionStorage
sessionStorage.getItem('message') // "You are great"

// Overwrite the message key in sessionStorage
sessionStorage.setItem('message', 'You are amazing')

// Retrieve data from sessionStorage
sessionStorage.getItem('message') // "You are amazing"

As you can see, overwriting data in sessionStorage is easy. Just like with localStorage, you will not be alerted that you are about to overwrite data. Once data has been overwritten it cannot be retrieved.

Deleting Data from Session Storage

Data is automatically deleted if the user leaves the session by closing their browser window. If you want to manually delete specific keys, then the same principles as the ones for localStorage apply. The removeItem() method is the way to go.

sessionStorage.removeItem('message')

Using the removeItem() method will remove the key and accompanying data if it exists. If the key does not exist, you will not get an error. Once a key-value pair has been removed using the removeItem() method, it is gone forever and the contents of that specific key cannot be retrieved.

Deleting All Data from Session Storage

Like when using localStorage, you may come across a situation in which you wish to delete all of the key-value pairs in your sessionStorage store. The process is exactly the same and has the same exact outcome and consequences. Simply call the clear() method and you are good to go.

sessionStorage.clear()

Note that once the user ends their session with your application, all the data that is stored in sessionStorage will automatically be deleted anyway.

Session Storage Use Considerations

The sessionStorage API is meant for storing session specific data. Here are some things consider when deciding what to put in localStorage:

  • Do not store any sensitive data in sessionStorage.
  • Any data stored in sessionStorage will automatically be deleted once the users ends the session by closing the browser tab or window.
  • Any JavaScript code on your page has full and unrestricted access to sessionStorage.
  • The data in sessionStorage is meant to be consumed client-side, for example, managing state or figuring out where to redirect.
  • While many browsers support 10MB of storage or more, Firefox limits sessionStorage to 5MB, so for compatibility you should limit the amount of data you put in sessionStorage to 5MB.
  • The Web Storage API is synchronous. This could affect performance if you have a lot of calls back and forth to sessionStorage.

Other Methods and Properties Available via Web Storage API

The Web Storage API is fairly small. There are just two more methods and properties that both localStorage and sessionStorage can call. The key() method and the length property.

The key() method when executed on localStorage or sessionStorage requires a number to be passed as a parameter. What will be returned is the corresponding key value. Let’s see an example of how this works.

// Let's store some data in localStorage
localStorage.setItem('heroName', 'Leroy Jenkins')
localStorage.setItem('mainWeapon', 'Sword')
localStorage.setItem('secondaryWeapon', 'Bow')
localStorage.setItem('health', '100')

// Let's use the .key() method
localStorage.key(0) // Should return "heroName"
localStorage.key(2) // Should return "secondaryWeapon"

Using the key() method with sessionStorage works the same.

// Let's store some data in sessionStorage
sessionStorage.setItem('title', 'Avengers: Endgame')
sessionStorage.setItem('year', '2019')
sessionStorage.setItem('budget', '250000000')
sessionStorage.setItem('villain', 'Thanos')

// Let's use the .key() method
sessionStorage.key(1) // Should return "year"
sessionStorage.key(3) // Should return "villain"

A very important thing to note is that the order of the keys cannot be guaranteed and is left to the browser to decide how and in what order to store. For this reason, it is not recommended that you rely on the key() method to get you the exact key you are looking for. The most common use case on the other hand, would be to give you a list and name of all the keys available in either localStorage or sessionStorage. I’ll show you how to do that once we learn about the length property.

The length property returns the number of data items that are stored in either localStorage or sessionStorage.

// Let's store some data in localStorage
localStorage.setItem('name', 'Ado Kukic')
localStorage.setItem('city', 'Las Vegas')
localStorage.setItem('state', 'Nevada')
localStorage.setItem('country', 'USA')

// Let's use .length to see how many items we have in localStorage
localStorage.length // Should return 4

Likewise, we can do the same with sessionStorage. Here’s an example:

// Let's store some data in sessionStorage
sessionStorage.setItem('name', 'Klaus')
sessionStorage.setItem('type', 'Dog')
sessionStorage.setItem('breed', 'Siberian Husky')

// Let's use .length to see how many items we have in sessionStorage
sessionStorage.length // Should return 3

As promised earlier, I will show you how to combine the length property and key() method to display a list of all current keys stored in the localStorage of your application. The code is as follows:

// Get the length of our localStorage
let items = localStorage.length

// Create a loop that runs once for every item in localStorage
for (let i = 0; i < items; i++) {
  // Print out the key value of the data
  console.log(localStorage.key(i))
}

If you wanted to print out the actual key-value pair, that can be done easily as well. In this example, we’ll use sessionStorage. We’ll also optimize our code a little bit.

// Create a loop that runs once for every item in sessionStorage
for (let i = 0; i < sessionStorage.length; i++) {
  let key = sessionStorage.key(i)
  let value = sessionStorage.getItem(key)

  // Print out the key and data
  console.log(`The key ${key} has the value of ${value}`)
}

Further Reading

This article was pretty exhaustive in covering the entire Web Storage API and use cases, but if you would like to learn more about the spec, browser compatibility, or history check out the following resources:

Conclusion

This article covered everything there is to know about the Web Storage API. The web is quickly evolving and new features are proposed and added regularly. The Web Storage API is one of the fundamental features of the modern browser and I hope that this article can be a great reference point when working with localStorage and sessionStorage in your applications.

Newsletter

Subscribe to the newsletter and be the first to know when I publish new content. No Spam.