webStorage: Persistent client side data storage

Until recently, the only ways to maintain a user’s data between visits to your site have been to store it on the server, or use cookies in the browser. Both present significant security challenges and quite a good deal of effort for us as developers.

Cookies are designed for communication between the browser and a server that persists between sessions. They’re typically used for identifying a user on return visits and storing details about that user. Cookies are sent between the browser and server in plain text, unencrypted, each time the user opens a page. So, unless an application encrypts cookie contents, these can be quite trivially be read particularly on public wifi networks, when used over standard HTTP (though less easily over encrypted HTTPS).

Storing all client data on the server creates usability issues as well, as users need to log in each time they use that site. And of course the heavy lifting of ensuring data is secure during transmission, and on the server is left to you as the developer. And it’s rather tricky to build apps which work when the user is offline if the user’s data is all stored on the server.

As web applications become increasingly sophisticated, developers need ways to keep data around in the browser (particularly if we want our applications to work when the user is offline).

Two closely related but slightly different W3C technologies exist to help keep track of information solely in the browser. They enable far more structured data than cookies, are much easier for us to use as developers, and the information stored can only be transmitted to a server explicitly by the application.

sessionStorage stores data during a session and is removed once a session is finished. localStorage is almost identical, but the data stored persists indefinitely, until removed by the application. Let’s start with sessionStorage, keeping in mind that we use localStorage almost identically.

sessionStorage

What is a session?

The key feature of sessionStorage is that data only persists for a session. But just what is a session? HTML5 has the concept of a “top-level browsing context“. This is, in essence, a window or tab. A session lasts for that top-level browsing context while it is open, and while that top-level browsing context is pointed at the same domain (or strictly speaking, the same origin).

During the session, a user could visit other pages of the domain, or other sites entirely, then return to the original domain. Any data saved in sessionStorage during that session will remain available, but only to pages in the original domain, until the tab or window is closed.

If the user opens a link to your site in another tab or window, then there is no access to this sessionStorage, since this new tab or window is a new session.
It’s worth noting that sessionStorage is also shared with pages inside subframes in the same domain as the top level document in the window.

So, if we

  • visit http://www.webdirections.org in a tab and save data to sessionStorage
  • then follow a link to http://westciv.com in this same tab
  • and then return to http://www.webdirections.org in the same tab
  • we return to the same session for http://www.webdirections.org
  • the data in the original sessionStorage is still available

If however we

  • visit http://www.webdirections.org in a tab and save data to sessionStorage
  • then follow a link to http://www.webdirections.org in a new tab
  • the data in the original sessionStorage is not available to this new tab (but is in the original tab)

The one exception to this is when a browser crashes, and is restarted. Typically, browsers will in this case reopen all the windows that were open when the browser crashed. The specification allows in this situation for sessionStorage to persist for reopened windows from before the crash (WebKit, Mozilla and Opera browsers support this, IE8 does not, though IE9 and up do).

Which may sound like a great boon for the user, but, as an application developer, you may wish to consider whether you in fact want to persist session data after a crash. A user may consider that when their browser crashes using a service like web mail or online banking at an internet café or other shared computer that their login details have been purged, but if these were stored in sessionStorage then the next user to launch the browser will resume the session that were current when the user crashed. Ruh-roh.

What good is sessionStorage?

So, what good is sessionStorage? Well, one very useful application would be to maintain sensitive information during a transaction, signup, sign in and so on, which will be purged as soon as the user closes a window or tab. It can be used to create a multi-page form or application, where the information in each page can persist, and then be sent to the server all at once when the transaction is complete. It also moves some of the heavy lifting for protecting sensitive data away from application developers to the browser developer.

Using sessionStorage

sessionStorage is a property of the window object in the DOM. Because it is as yet not universally supported, we’ll want to check that this property exists before we use it:

if (window.sessionStorage) {
  //we use sessionStorage
}

else {
  //we do something else, perhaps use cookies, or another fallback
}

Right, so now we have our sessionStorage object, how do we use it?

Key-Value Pairs

sessionStorage stores “key-value pairs”. Each pair is a piece of information (the value), identified by a unique identifier (the key). Both the key and the value are strings (more on the implications of this in a moment).

We use the setItem method of the sessionStorage object to store data like so:

//get the value of the input with id="name"
var name = document.querySelector('#name').value;

//store this value with the key "name"
window.sessionStorage.setItem('name', name);

Now we’ve stored the value of the input “name” in an item of the sessionStorage object called ‘name’. It will remain there until this window or tab is closed and it will then automatically be purged by the browser when the user closes the window or tab.

reading from sessionStorage

There’s not much point in storing these details if we can’t get them back at some point. We do this by using the function getItem of the sessionStorage object, using a single parameter, the key we used to set the item.

So, to get the value of the name, we’d use:

var name = window.sessionStorage.getItem('name');
Non-existent items

Now, what happens if for some reason there’s no item in sessionStorage with the key we are trying to access? In place of a string value, it returns null, not the empty string. So, it’s worthwhile testing whether the result returned is not null before using it

var email = window.sessionStorage.getItem('email');
if(email!==null){
  document.querySelector('#email').innerHTML = email;
}

Saving Data Between Sessions

When information is less sensitive, it may make sense to store it between sessions. Particularly as web sites become more application-like, and can increasingly work offline, saving preferences or the state of a document can make for much better usability.

My HTML5 and CSS developer tools do just this, using localStorage. That way, when someone returns to the tools, the last gradient or layout or transformation they built is waiting for them.

Best of all, using localStorage, for persistence between sessions, is almost identical to using sessionStorage.

window.localStorage

Instead of the sessionStorage object of the window, we use the localStorage object. All the methods of localStorage are the same as sessionStorage.

  • we set items with setItem
  • we get items with getItem

Because items on the localStorage will persist forever, we may want to delete them. We can do this with localStorage.removeItem(key), using the key for the item we want to remove.

If we want to delete the entire localStorage, we can use localStorage.clear(). But, be warned, anything your app has saved to localStorage for this user is gone for good.

sessionStorage in fact also has these methods, though we’re less likely to need them, as all of a sessionStorage is discarded by the browser when a session finishes.

Gotchas, Tips and Tricks

sessionStorage and localStorage store all data as strings

As mentioned earlier, the values stored in local and session storage are strings, which has a number of implications for developers.

In particular, when we store boolean values, integers, floating point numbers, dates, objects and other non-string values, we need to convert to and from a string when writing to and reading from storage.

There’s also a more subtle side effect of storing values as strings. JavaScript strings are UTF-16 encoded, which means each character is 2 bytes (in UTF-8 characters are one byte). This effectively halves the available storage space.

Private Browsing

Many browsers now have private (or ‘incognito’) browsing modes, where no history or other details are stored between sessions. In this situation, what happens with sessionStorage and localStorage varies widely by browser.

  • Safari returns null for any item set using localStorage.setItem either before or during the private browsing session. In essence, neither sessionStorage nor localStorage are available in private browsing mode.
  • Chrome and Opera return items set previous to private (“incognito”) browsing commencing, but once private browsing commences, treat localStorage like sessionStorage (only items set on the localStorage by that session will be returned) but like localStorage for other private windows and tabs
  • Firefox, like Chrome will not retrieve items set on localStorage prior to a private session starting, but in private browsing treats localStorage like sessionStorage for non-private windows and tabs, but like localStorage for other private windows and tabs

Getters and Setters

In addition to using getItem and setItem we can use a key directly to get and set an item in sessionStorage and localStorage, like so (where the key is “keyName”):

var itemValue = window.localStorage.keyName;

localStorage and sessionStorage Limits

The webStorage specification recommends browsers implement a 5MB limit on the amount of data localStorage or sessionStorage can save for a given domain. If you try to exceed the limit that various browsers have in place (for some browsers users can change this allowance) setItem throws an error. There’s no way of asking localStorage for the amount of space remaining, so it’s best to set item values with a try and catch for any error:

try {
	window.localStorage.setItem(key, value);
}
catch (exception) {
	//test if this is a QUOTA_EXCEEDED_ERR	

}

If the available space for this localStorage is exceeded, the exception object will have the name "QUOTA_EXCEEDED_ERR" and a code of 22.

As mentioned, in JavaScript strings are UTF-16 encoded, which means they are 2-byte. So, when saving the string “John”, we are actually using 8 bytes, not 4. Which means instead of 5MB of storage space per storage area, we effectively have 2.5MB.

If the storage needs of your application are likely to exceed 5MB, then web databases are likely to be a better solution. However, the situation with web databases is complicated, with two different standards, one, webSQL widely supported but deprecated, and the other IndexedDB, currently supported only in Firefox, Chrome and IE10.

Storage Events

We can add an event listener to the window for storage events so that when a storage object has been changed (there’s a reason for the emphasis) then we can be notified and respond to those changes.

window.addEventListener('storage', storageChanged, false);

Here, when localStorage is changed (by setting a new item, deleting an item or changing an existing item) our function storageChanged(event) will be called. The event passed as a parameter to this function has a property storageArea, which is the window’s localStorage object.

There are two things to be aware of with storage events.

  • The event only fires if the storage is changed (not if it is simply accessed and not if we set an item to the same value that it currently has!)
  • In the specification, the event is not received in the window or tab where the change occurred, only in other open windows and tabs that have access to this localStorage. Some browsers have implemented storage events in such a way that the event is also received by the window or tab which causes the change, but don’t rely on this.

webStorage Performance

Of late, a number of high profile, widely read articles critical of localStorage have been published, centring on its asserted performance shortcomings. The key criticism relates to the fact that webStorage is synchronous. This means a script using sessionStorage or localStorage waits while getItem, setItem and other storage methods are invoked. In theory, this can impact both the browser’s response to user input and execution of JavaScript in a page. In practice, I’d argue that this is not likely to be a significant problem for most cases.

I recently conducted some testing across a number of devices and browsers which demonstrates that even for poorly implemented code that does a very significant number of getItem and setItem operations, the performance of webStorage is unlikely to have significant impact.

Origin restrictions

We said earlier that that sessionStorage and localStorage are restricted to windows or tabs in the same domain, but in fact, the restriction is tighter than simply the top-level domain (such as webdirections.org)

To have access to each other’s webStorage, tabs or windows must have the same top-level domain (for example webdirections.org), subdomains (for example test.webdirections.org), and protocol (https://webdirections.org has a different localStorage from http://www.webdirections.org).

At first glance this might seem overly restrictive but imagine john.geocities.com having access to the localStorage of fred.geocities.org?

Browser Support and Backwards Compatibility

localStorage and sessionStorage are widely supported, from IE8 upwards and in all modern browsers, including mobile devices.

There are also several polyfills that allow for webStorage in browsers which don’t support it natively.

The Wrap

webStorage solves a long standing challenge for web developers – reliably and more securely storing data between sessions entirely on the client side. While there are assertions that performance limitations make localStorage “harmful”, in the real world, services like Google and Bing are using localStorage and performance experts like Steve Souders and Nicholas Zakas defend and advocate their use. That’s not to say webStorage is perfect or ideal in all situations. The synchronous nature of the API and 5MB limit per origin do mean that in certain circumstances an alternative may be required. webStorage is however eminently usable for a great many client side data storage needs.

Further Reading

10 responses to “webStorage: Persistent client side data storage”:

    • By: Xander
    • March 30th, 2012

    Awesome stuff as always John. Great level of detail and really practical use cases (and just as importantly the cases it won’t work!).

    • By: John
    • March 30th, 2012

    Thanks Xander

    that’s definitely my aim – to make it as real world applicable as posible!

    j

    • By: PhistucK
    • March 30th, 2012

    Just one quick correction – IndexedDB is supported in Chrome as well (webkitIndexedDB).

    • By: Sebastian
    • March 30th, 2012

    If you want to sync your localStorage data over several devices, and do it on a user-defined storage instead of your own, I recommend looking into Unhosted and their remoteStorage spec and library:

    http://unhosted.org/
    https://github.com/unhosted/remotestorage.js

    • By: John
    • April 1st, 2012

    Thanks! Still a way off widespread support, but good to see it progressing.

  1. Great article, we’ve done a good bit of work in normalizing the different storage API’s and released http://amplifyjs.com/api/store/ which normalizes them all. It’s a super lean API with a ton of power.

    • By: John
    • April 10th, 2012

    Thanks Jonathan,

    that’s great stuff!

    john

  2. […] Explorer 5The HTML5 drag and drop disasterFontdragrGetting off(line) — John Allsopp (@johnallsopp)webStorage: Persistent client side data storageGet off(line) articleDesigning in the browser — Divya Manian (@divya)Presentation SlidesStephen […]

  3. […] and even work in all modern browsers and devices, including IE8 up!Resources from this presentationwebStorage: Persistent client side data storage — tutorial by John AllsoppGet off(line) — tutorial by John AllsoppAbout John AllsoppJohn Allsopp has spent more than […]

    • By: George
    • September 27th, 2012

    Hello,

    Chrome seems to preserve sessionStorage across different domains. I am using sessionStorage in my Chrome plugin at https://chrome.google.com/webstore/detail/cjnnmmjgjaaomkcjibnncokikbianjap