Channels ▼
RSS

Web Development

The localStorage API


In the first article of this series, Understanding Client-Side Storage in Web Apps, I reviewed the HTML5 storage options and concluded that the localStorage API is the most viable option. Now, in this second and final part of this series, I'll dive deep into that API and show ways to use it to improve your websites.

The localStorage API is very simple — it's basically an associative array (hash map) with wrapper methods to set and get items, iterate through all the stored items, and throw exceptions that notify the developer when something goes wrong, such as going over quota.

The table below shows the methods and attributes, based on the official WebStorage specification.

method/attribute args returns
setItem String key, String value
getItem String key

String value
removeItem String key
clear
key int index String key
length int length

Security

The API is subject to the "same origin" policy, with which most developers are familiar thanks to XMLHttpRequest. That policy dictates which websites can see what data based on matching the subdomains and ports of a URL.

For example, given the URL "http://store.company.com/dir/page.html," here's a summary of who could view locally stored data:

http://store.company.com/dir2/other.html Yes
http://store.company.com/dir/inner/another.html

Yes
https://store.company.com/secure.html No (Different protocol)
http://store.company.com:81/dir/etc.html No (Different port)
http://news.company.com/dir/other.html No (Different host)

From a security standpoint, this means that you need to be careful not to use localStorage on a shared domain (because every page on that domain will have access to the data), and to be aware of security exploits like DNS spoofing. You should also know that your locally stored data will not be shared across subdomains unless you do a bit of magic with document.domain. You can read more about the same-origin policy on Mozilla's Developer Network.

Abstraction Libraries

The localStorage API is quite easy to use, and you could use it directly via the API. But, as with many JavaScript Web APIs, there are several reasons why you might want to use a localStorage abstraction library; and there are an increasing number of libraries available.

Most of them include a feature-detection check to see whether the browser actually supports the API and whether it's currently enabled (as browsers let users disable it, just like with cookies). In the case that the feature check fails, some libraries fallback to using other techniques (such as cookies or browser-specific hacks). Most libraries handle serialization of objects using JSON, and some libraries are designed for specific use cases. Let's take a look at a few of them.

store.js

store.js is probably the most popular localStorage library, and it's also a great example of what to expect from a library. After it checks for browser support, it uses fallbacks to globalStorage for older Firefox versions and userData for older IE versions — two browser-specific client-side storage APIs that never made it into the standards world.

In the example code below, we store an object representing a song in localStorage (which is then serialized by store.js), retrieve and display it, and then remove it.

store.set('song', {artist: 'Roxette',
                   title: 'Listen to your heart'})
var song = store.get('song');
document.getElementById('song').innerHTML = 
    song.artist + ' sings "' + song.title + '"';
store.remove('song');

Lawnchair

Another popular wrapper library is Lawnchair. It's similar in capabilities but its get/set methods are all asynchronous, so that it can be used with both synchronous and asynchronous APIs. It features "adapters," which let you use it with various client-side storage options, including older options such as userData but also uber modern options like IndexedDB. It also includes a plugin API and plugins for common tasks like aggregation, pagination, and queries.

In the example code below, we store an object representing a band, and then we retrieve and display it inside a callback function. When Lawnchair is used with localStorage, the callback is called pretty much instantly, since it's a synchronous API. If you used Lawnchair with something like IndexedDB, it may actually take a few seconds before the callback is called.

var bands = Lawnchair(function() {
  this.save({key:'abba', name: 'ABBA', hometown: 'Stockholm'});

  this.get('abba', function(band) {
    document.getElementById('band').innerHTML = 
        band.name + ' is from ' + band.hometown;
  });
});

lscache

The lscache library is written with a particular use case in mind — in this case, expiration. The library lets you set expiration times when storing items in localStorage, so that you can kick items out after a particular amount of time. Its API mimics the memcache API, a popular server-side caching API, and its use cases are similar. Later, I'll explore when you would actually want to use localStorage in this way.

In the example code below, I store an array of objects representing songs (such as we might retrieve from an API), set those songs to expire in 24 hours, and display them on the page.

Full disclosure: This is the library I wrote, and is the one I use most frequently in my web apps. It's certainly not the best API for every use case, however, especially because it does not yet include a fallback for older browsers.

var songs =
[{artist: 'Ansiktet', title: 'Ackligt'},
 
{artist: 'Norlie & Kkv', title: 'Trojan Du Hatar'},

 {artist: 'Michel Telo', title: 'Ai Se Que Te Pego'},
 
{artist: 'Avicii', title: 'Levels'},
 {artist: 'Flo Rida & Sia', title: 'Wild Ones'},
 
{artist: 'Moa Lignell', title: 'When I Held Ya'},
 {artist: 'David Guetta & Sia', title: 'Titanium'},
 
{artist: 'Timbuktu', title: 'Flickan '},
 {artist: 'Rihanna & Calvin Harris', title: 'We Found Love'},
 
{artist: 'Takida', title: 'You Learn'}];

 lscache.set('swedentop10', songs, 60*24);

 songs = lscache.get('swedentop10');

 for (var i = 0; i < songs.length; i++) {
   var song = songs[i];
   document.getElementById('songs').innerHTML += 
   song.artist + ': ' + song.title;
}

What can we use localStorage for?

We can use localStorage for some of the things we used to use cookies for. We can also use it for a wider range of uses cases to help us improve the user experience for our websites — which is presumably what we're all striving for. With it, we can remember user data, retain application state, remember form input, improve performance, and help the app work offline.

Remembering User Data

One reason to use localStorage is to remember user data. While we still want to remember most user data on the server, there are some cases where you might want to store data on the client instead:

  • If you want to build an app without a server (maybe for prototyping),
  • If you want to give users a customized experience before forcing them to create an account, or
  • If you're building an app that inherently lives only in a client (like browser extensions or mobile apps).

Example: dillinger.io

dillinger.io is a Markdown editor that uses localStorage to remember user preferences and the most recently typed text, so that you can use it without ever signing in. It gives users the option to sign in with Github, and ideally it would migrate the user data from the client to the server once you signed in, so that it would follow you across platforms.

Here's an example of what they store in localStorage:

profile {"theme":"ace/theme/textmate","showPaper":false, "currentMd":"lscache\nThis is a simple library that emulates 'memcache' functions using HTML5 'localStorage',…

Note that in this and the following examples, the key values are often truncated for readability. To see everything they store, visit the site themselves and inspect their localStorage using the resources panel in Chrome Developer Tools.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 
Dr. Dobb's TV