Mathias Bynens

How I detect and use localStorage: a simple JavaScript pattern

· tagged with JavaScript, performance

Disclaimer: This is not a localStorage tutorial — see Chapter 7 of Dive Into HTML5 if that’s what you’re looking for. Read on if you’re still interested.

The conventional, old-school method

Here’s how you can detect localStorage support:

// Feature test
var hasStorage = (function() {
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
} catch(e) {
return false;
}
}());

Note that accessing the global localStorage object may throw an exception, so it needs to be done inside a try-catch block for this test.

Modernizr does it this way (and documents the technique extensively in the source). It could be simplified a bit depending on the browsers and edge cases you wish to support, but for now this is the most robust feature detect out there.

With the above snippet, you can make use of localStorage in your code as follows:

if (hasStorage) {
// Use `localStorage` here, e.g.
localStorage.setItem('foo', 'bar');
localStorage.lol = 'wat';
localStorage.removeItem('foo');
}

Of course, this is nothing new. So why am I writing this? Well, I’ve been using a slightly different technique that has a few advantages, and I’d like to share it.

The new pattern

Let’s see what happens if we tweak the above script just a little bit:

// Feature detect + local reference
var storage = (function() {
var uid = new Date,
result;
try {
localStorage.setItem(uid, uid);
result = localStorage.getItem(uid) == uid;
localStorage.removeItem(uid);
return result && localStorage;
} catch(e) {}
}());

Compared to the previous snippet, not much has changed. There are three differences:

We could take it one step further and avoid the repeated scope lookups for localStorage (if it’s available) as follows:

// Feature detect + local reference
var storage = (function() {
var uid = new Date,
storage,
result;
try {
(storage = window.localStorage).setItem(uid, uid);
result = storage.getItem(uid) == uid;
storage.removeItem(uid);
return result && storage;
} catch(e) {}
}());

As Juriy “kangax” Zaytsev noted, it’s possible to avoid the anonymous function entirely:

// Feature detect + local reference
var storage,
fail,
uid;
try {
uid = new Date;
(storage = window.localStorage).setItem(uid, uid);
fail = storage.getItem(uid) != uid;
storage.removeItem(uid);
fail && (storage = false);
} catch(e) {}

It works just as well, and is even more efficient than the previous snippet because of the reduced number of function calls.

Anyhow, you can use any of these snippets like this:

if (storage) {
// Use `storage` here, e.g.
storage.setItem('foo', 'bar');
storage.lol = 'wat';
storage.removeItem('foo');
}

It couldn’t be simpler!

Useful?

I mostly like this pattern because of its elegance, but — assuming your code is inside its own scope to prevent leaking variables to the global scope — there are some (minor) performance benefits as well:

Other use cases

I’ve been using localStorage (and sessionStorage) in this example, but of course the exact same pattern could be applied in other cases as well. Can you think of other examples?

Comments

Paul Neave wrote on :

I think the try-catch block is only for a bug in Firefox 4 beta, which has long been squashed. I simply use if (!!localStorage.getItem) which works well enough for me.

4esn0k wrote on :

Paul Neave: Spec says, that implementations may throw error, when accessing window.localStorage and user disable localStorage

localStorage.setItem, localStorage.removeItem can throw exceptions! Wrapping it with try-catch may be better.

Paul Neave wrote on :

Mathias: Yes, I read the Modernizr source - I think they're being overcautious for the sake of Firefox 4 beta (which nobody uses anymore). I use the !! as a way of "truthyfying" the statement a bit more, so it's not undefined but false, and can be stored in a variable like you suggested.

No harm in being overcautious, but it's an edge case for me.

4esn0k: I'd like to see which browsers actually do break when being tested for local and session storage. AFAIK it's only Firefox 4 beta that breaks?

kangax wrote on :

If you’re aliasing localStorage to global storage variable, why not avoid all this anonymous function cruft in the first place? :)

try {
if (localStorage.getItem) {
var storage = localStorage;
}
} catch(e) { }

or even (a little more concise but also more cryptic):

try { 
localStorage.getItem && (window.storage = localStorage);
} catch(e) { }

So once this snippet is executed, storage is either undefined — if either 1) localStorage doesn't exist, 2) localStorage.getItem is falsy, 3) localStorage.getItem throws — or it references same object as localStorage (if execution got to the assignment part).

Mathias wrote on :

kangax: I’m assuming the code is inside its own scope (to prevent leaking variables to the global scope), so storage isn’t supposed to be global. That said, your first suggestion would still work fine in a local scope! I’ve amended the article. Thanks!

John-David Dalton wrote on :

Update by Mathias: This comment was in response to the feature test that Modernizr was using before, which was basically something like:

// Feature test
var hasStorage = (function() {
try {
return !!localStorage.getItem;
} catch(e) {
return false;
}
}());

Modernizr later switched to a more robust feature detect instead (similar to the one John-David suggested here), and this post has since been rewritten.


Speaking of localStorage being disabled… This snippet will determine if it’s disabled and return false instead of letting your code eventually facepalm on the disabled storage (tested in Chrome 12).

var storage = !!function() {
var result,
uid = +new Date;
try {
localStorage.setItem(uid, uid);
result = localStorage.getItem(uid) == uid;
localStorage.removeItem(uid);
return result;
} catch(e) {}
}() && localStorage;

Paul Irish wrote on :

Paul Neave: I just tested in Aurora with dom.storage.enabled set to false (and surprisingly, WAY too many FF users do). It no longer throws exceptions when you just try if (window.localStorage) so that's good! It now just returns a null object, which is pleasant. Dunno about FF4, but I don’t think the superfluous exceptions got resolved in the FF4 beta cycle… Not sure…

Anyway we’ll keep an eye on it and hope to remove the try/catch soon.

Mathias wrote on :

Paul Irish: IMHO the try-catch should never be removed from this feature test. The Web Storage specification says:

The user agent may throw a SECURITY_ERR exception instead of returning a Storage object if the request violates a policy decision (e.g. if the user agent is configured to not allow the page to persist data).

[…]

If the Document’s origin is not a scheme/host/port tuple, then throw a SECURITY_ERR exception and abort these steps.

[…]

User agents must raise a SECURITY_ERR exception whenever any of the members of a Storage object originally returned by the localStorage attribute are accessed by scripts whose effective script origin is not the same as the origin of the Document of the Window object on which the localStorage attribute was accessed. This means Storage objects are neutered when the document.domain attribute is used.

Kurt wrote on :

Thank you.

I am working on an implementation myself. One thought is to have it return an array should it otherwise be false. As I don’t care where this data is being persisted, I will always have a storage object to work with.

Do you foresee any issues with such approach?

var _userCache = ((function() {
try {
return localStorage.getItem;
} catch (e) {}
}()) && localStorage) || {};

Kurt wrote on :

I realize my example was somewhat, to say the least, contrived. But, the idea was to have a single object without having to create a set of wrapper functions — which doesn’t make any sense now that I think about it. I need to implement a set of wrappers regardless. What can I say… It’s Friday afternoon!

Kurt wrote on :

I actually did find a way in the end, which has been working in a prototype:

var _userCache = (function() {
try {
if (localStorage.getItem) {
return localStorage;
}
} catch (e) {
return {};
}
}()) && localStorage || {};

Mathias wrote on :

Kurt: Not sure what you’re trying to get at, but you could just do:

var _userCache = (function() {
try {
var storage = localStorage;
return storage.getItem ? storage : {};
} catch (e) {
return {};
}
}());

However, that still won’t allow you to do e.g. _userCache.getItem('foo') if localStorage is disabled or unsupported.

// Assume `localStorage` is disabled or unsupported
_userCache.setItem('foo', 'bar'); // ReferenceError
_userCache.foo = 'bar';
_userCache.getItem('foo'); // ReferenceError
_userCache.foo;
_userCache.removeItem('foo'); // ReferenceError
delete _userCache.foo;
_userCache.clear(); // ReferenceError
_userCache = {};

I’m not sure how useful that is though, seeing as the _userCache object will be lost as soon as you close the page.

Mathias wrote on :

Paul: Thanks for the heads up! Since this post used the Modernizr feature detect as its base example, I’ve now rewritten the whole thing to use the updated code.

Phil L wrote on :

Why not just test for localStorage first, using if (typeof Storage !== 'undefined')? Yes, you gotta use typeof, otherwise IE 7 throws a fit.

Mathias wrote on :

Phil: That’s a way to use feature detection. The snippet this blog post is about performs feature testing which is more robust. For example, an iOS device with private browsing mode enabled would pass your test, even though you can’t actually use localStorage in those conditions.

Simon wrote on :

The final update about removing the anonymous function seems to break it (iOS7 Safari Private mode) because (storage = window.localStorage) succeeds, but the addItem throws an exception, therefore leaving storage as truthy. The snippet before that seems fine though. See for proof: http://i.imgur.com/1XW0K9Z.png

Leave a comment

Comment on “How I detect and use localStorage: a simple JavaScript pattern”

Some Markdown is allowed; HTML isn’t. Keyboard shortcuts are available.

It’s possible to add emphasis to text:

_Emphasize_ some terms. Perhaps you’d rather use **strong emphasis** instead?

Select some text and press + I on Mac or Ctrl + I on Windows to make it italic. For bold text, use + B or Ctrl + B.

To create links:

Here’s an inline link to [Google](http://www.google.com/).

If the link itself is not descriptive enough to tell users where they’re going, you might want to create a link with a title attribute, which will show up on hover:

Here’s a [poorly-named link](http://www.google.com/ "Google").

Use backticks (`) to create an inline <code> span:

In HTML, the `p` element represents a paragraph.

Select some inline text and press + K on Mac or Ctrl + K on Windows to make it a <code> span.

Indent four spaces to create an escaped <pre><code> block:

    printf("goodbye world!"); /* his suicide note
was in C */

Alternatively, you could use triple backtick syntax:

```
printf("goodbye world!"); /* his suicide note
was in C */
```

Select a block of text (more than one line) and press + K on Mac or Ctrl + K on Windows to make it a preformatted <code> block.

Quoting text can be done as follows:

> Lorem iPad dolor sit amet, consectetur Apple adipisicing elit,
> sed do eiusmod incididunt ut labore et dolore magna aliqua Shenzhen.
> Ut enim ad minim veniam, quis nostrud no multi-tasking ullamco laboris
> nisi ut aliquip iPad ex ea commodo consequat.

Select a block of text and press + E on Mac or Ctrl + E on Windows to make it a <blockquote>.