Categories
html javascript local-storage

How to store objects in HTML5 localStorage

2933

I’d like to store a JavaScript object in HTML5 localStorage, but my object is apparently being converted to a string.

I can store and retrieve primitive JavaScript types and arrays using localStorage, but objects don’t seem to work. Should they?

Here’s my code:

var testObject = { 'one': 1, 'two': 2, 'three': 3 };
console.log('typeof testObject: ' + typeof testObject);
console.log('testObject properties:');
for (var prop in testObject) {
    console.log('  ' + prop + ': ' + testObject[prop]);
}

// Put the object into storage
localStorage.setItem('testObject', testObject);

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('typeof retrievedObject: ' + typeof retrievedObject);
console.log('Value of retrievedObject: ' + retrievedObject);

The console output is

typeof testObject: object
testObject properties:
  one: 1
  two: 2
  three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]

It looks to me like the setItem method is converting the input to a string before storing it.

I see this behavior in Safari, Chrome, and Firefox, so I assume it’s my misunderstanding of the HTML5 Web Storage specification, not a browser-specific bug or limitation.

I’ve tried to make sense of the structured clone algorithm described in 2 Common infrastructure. I don’t fully understand what it’s saying, but maybe my problem has to do with my object’s properties not being enumerable (???).

Is there an easy workaround?


Update: The W3C eventually changed their minds about the structured-clone specification, and decided to change the spec to match the implementations. See 12111 – spec for Storage object getItem(key) method does not match implementation behavior. So this question is no longer 100% valid, but the answers still may be of interest.

5

  • 22

    BTW, your reading of “structured clone algorithm” is correct, it’s just that the spec was changed from string-only values to this after the implementations were out. I filed bug bugzilla.mozilla.org/show_bug.cgi?id=538142 with mozilla to track this issue.

    – Nickolay

    Jan 6, 2010 at 13:30

  • 2

    This seems like a job for indexedDB…

    Aug 2, 2013 at 2:06

  • 1

    How about storing an array of Objects in localStorage? I am facing same problem that it is getting converted to string.

    Jun 9, 2017 at 19:08

  • 1

    could you instead just serialize the array? like store with JSON stringify then parse again upon loading?

    – brandito

    Mar 12, 2018 at 6:35


  • 1

    You can use localDataStorage to transparently store javascript data types (Array, Boolean, Date, Float, Integer, String and Object)

    – Mac

    Jun 11, 2018 at 0:54

3674

Looking at the Apple, Mozilla and Mozilla again documentation, the functionality seems to be limited to handle only string key/value pairs.

A workaround can be to stringify your object before storing it, and later parse it when you retrieve it:

var testObject = { 'one': 1, 'two': 2, 'three': 3 };

// Put the object into storage
localStorage.setItem('testObject', JSON.stringify(testObject));

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('retrievedObject: ', JSON.parse(retrievedObject));

7

  • 199

    do observe that any metadata will be removed. you just get an object with the key-value pairs, so any object with behaviour need to be rebuilt.

    – oligofren

    Oct 7, 2013 at 18:48

  • 7

    @CMS can setItem throw some exception if the data is over the capacity ?

    Mar 26, 2014 at 9:27

  • 3

    … applies to objects with circular references only, JSON.stringify() expands the referenced object to its full “content” (implicitly stringified) in the object we stringify. See: stackoverflow.com/a/12659424/2044940

    – CodeManX

    Jul 23, 2014 at 16:54

  • 3

    The problem with this approach are performance issues, if you have to handle large arrays or objects.

    – Mark

    Oct 29, 2014 at 9:35

  • 4

    @oligofren true, but as maja correctly suggested eval() => , this is one of the good use of , you can easily retrieve function code => store it as string and then eval() it back 🙂

    – jave.web

    Jul 6, 2015 at 20:03


663

A minor improvement on a variant:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    var value = this.getItem(key);
    return value && JSON.parse(value);
}

Because of short-circuit evaluation, getObject() will immediately return null if key is not in Storage. It also will not throw a SyntaxError exception if value is "" (the empty string; JSON.parse() cannot handle that).

10

  • 55

    I just want to quickly add the usage as it wasn’t immediately clear for me: var userObject = { userId: 24, name: 'Jack Bauer' }; And to set it localStorage.setObject('user', userObject); Then get it back from storage userObject = localStorage.getObject('user'); You can even store an array of objects if you want.

    – zuallauz

    Aug 16, 2011 at 21:38

  • 9

    It is just boolean expression. Second part are evaluated only if left one is true. In that case result of whole expression will be from right part. It is popular technic based on the way how boolean expressions are evaluated.

    – Guria

    Nov 1, 2011 at 21:04

  • 5

    I do not see the point of the local variable and the shortcut evaluation here (minor performance improvements aside). If key is not in the Local Storage, window.localStorage.getItem(key) returns null – it does not throw an “Illegal access” exception – and JSON.parse(null) returns null as well – it does not throw an exception either, neither in Chromium 21 nor per ES 5.1 section 15.12.2, because String(null) === "null" which can be interpreted as a JSON literal.

    Oct 8, 2012 at 10:20


  • 9

    The values in Local Storage are always primitive string values. So what this shortcut evaluation does handle is when someone stored "" (the empty string) before. Because it type-converts to false and JSON.parse(""), which would throw a SyntaxError exception, is not called.

    Oct 8, 2012 at 10:42


  • 2

    This wont’t work in IE8, so you’re better of using the functions in the confirmed answer if you need to support it.

    – Ezeke

    Jan 16, 2013 at 12:15


242

You might find it useful to extend the Storage object with these handy methods:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    return JSON.parse(this.getItem(key));
}

This way you get the functionality that you really wanted even though underneath the API only supports strings.

4

  • 15

    Wrapping CMS’ approach up into a function is a good idea, it just needs a feature tests: One for JSON.stringify, one for JSON.parse, and one to test if localStorage can in fact set and retrieve an object. Modifying host objects is not a good idea; I would rather see this as a separate method and not as localStorage.setObject.

    – Garrett

    Dec 9, 2010 at 1:28


  • 4

    This getObject() will throw a SyntaxError exception if the stored value is "", because JSON.parse() cannot handle that. See my edit to Guria’s answer for details.

    Oct 8, 2012 at 10:45


  • 13

    Just my two cents, but I’m pretty sure it’s not a good idea to extend objects provided by the vendor like this.

    – Sethen

    Jul 6, 2014 at 18:54


  • 1

    I completely agree with @Sethen . Please don’t monkey-patch globals implemented by the browser like this. It can break code and it’s not future-compatible with browsers that may ship a setObject method in this global in the future.

    – Flimm

    Dec 16, 2021 at 18:45