Remembering Data with localStorage

Make a page remember things between visits with localStorage, save whole lists with JSON, start cleanly when nothing is saved yet, and know what sessionStorage and cookies are for and what must never be stored in the browser.

Apprentice · JavaScript · Web Development · Updated

After this lesson you can

  • Save, read and remove a value in the browser with localStorage.setItem, getItem and removeItem
  • Save an array in localStorage with JSON.stringify, load it with JSON.parse, and start with an empty list when nothing is saved yet
  • Choose between localStorage, sessionStorage and a cookie, and keep passwords and private data out of the browser's storage

Key takeaways

  • localStorage keeps text under a key for your site, even after the browser closes. setItem saves, getItem reads, and removeItem deletes.
  • localStorage stores only strings. Turn an array or object into text with JSON.stringify before saving, and back with JSON.parse after loading.
  • getItem gives null when nothing is saved under that key yet, so start with an empty list in that case.
  • sessionStorage works the same way but forgets when the tab closes. Cookies are small pieces of text the browser sends to the server with every request.
  • Never store passwords, card numbers or anything private in the browser. Any script on the page, and anyone at that computer, can read it.

Add five books to the Reading List, press F5 to reload, and they're gone. A web page forgets everything when it reloads: every variable starts over. Yet plenty of sites remember you: the dark mode you picked last week, a game's high score, the playlist you were halfway through. Many of them do it with localStorage, a small store the browser keeps for each site. This lesson makes the Reading List remember its books between visits, and shows what you should never keep there.

A notebook the browser keeps for your site

Think of a locker at the gym: you put your things in under your locker number, and they're still there next time. The browser gives every site a small store like that, called localStorage. Each thing in it is saved under a key, a name you choose, and you get it back by the same key. It stays after the tab closes, after the browser closes, and after the computer restarts, until your code (or the person) removes it. (Safari is the exception: it clears a site's localStorage after seven days of use with no visit to that site.)

Three methods do the work:

localStorage.setItem("readerName", "Maya");
console.log(localStorage.getItem("readerName"));

localStorage.removeItem("readerName");
console.log(localStorage.getItem("readerName"));
  • setItem(key, value) saves the value under the key, replacing anything already there.
  • getItem(key) reads it back: here the Console shows Maya.
  • removeItem(key) deletes it. After that, getItem gives null: nothing is saved under that key.

Each site has its own store: a page on another site can't read yours. To see what's saved, open the developer tools (F12), choose Application (in Chrome and Edge) or Storage (in Firefox), then Local storage and your page's address. You can edit or delete entries there too.

Everything is stored as text

localStorage keeps strings only. Give setItem anything else and it turns it into text first, the way String() does. For a number that's mostly fine: save 320, and you get back "320", which needs Number() again. For a list it's a disaster:

const books = [{ title: "Dune", pages: 412 }];

console.log(String(books));
console.log(String(["Dune", "Emma"]));
console.log(String(320));
[object Object]
Dune,Emma
320

Save an array of books as it is, and what lands in storage is the text [object Object]: the books themselves are lost. The list of titles comes out as Dune,Emma, which can't be turned back into a list safely, since a title can have a comma in it.

Saving a list with JSON

You met the fix in JSON: Data You Can Save and Send. JSON.stringify turns an array or object into text that keeps all of it, and JSON.parse turns that text back into a real array or object:

const books = [
  { title: "Dune", pages: 412, status: "reading" },
  { title: "Emma", pages: 474, status: "want-to-read" },
];

const text = JSON.stringify(books);
console.log(text);

const loaded = JSON.parse(text);
console.log(loaded.length);
console.log(loaded[1].title);
[{"title":"Dune","pages":412,"status":"reading"},{"title":"Emma","pages":474,"status":"want-to-read"}]
2
Emma

So the page saves with JSON.stringify and loads with JSON.parse, and keeps the key in one constant so it's spelled the same both times:

const storageKey = "reading-list";

function saveBooks(books) {
  localStorage.setItem(storageKey, JSON.stringify(books));
}

function loadBooks() {
  return JSON.parse(localStorage.getItem(storageKey));
}

Call saveBooks(books) after every change: a book added, a status changed, a book removed. Call loadBooks() once, when the page starts. That loadBooks has a problem, though, on someone's very first visit.

When nothing is saved yet

The first time anyone opens the page, nothing is saved under "reading-list", so getItem gives null. And JSON.parse(null) doesn't fail. It quietly gives back null, which you can see in Node with the value getItem would give:

const saved = null;

console.log(JSON.parse(saved));

const books = JSON.parse(saved) ?? [];
console.log(books.length);
null
0

Without the fix, the page's first books.push(...) stops with TypeError: Cannot read properties of null (reading 'push'). With ?? [] (read "nullish coalescing", or "double question mark"), from Missing Values: null, undefined, ?. and ??, a first visit starts with an empty list.

One more thing can go wrong: the saved text can be broken, for example after someone edits it by hand in the developer tools. JSON.parse throws an error on text that isn't JSON, so a careful page catches it, with the try and catch from Handling Errors with try, catch and finally:

function booksFrom(saved) {
  if (saved === null) {
    return [];
  }
  try {
    return JSON.parse(saved);
  } catch (error) {
    return [];
  }
}

console.log(booksFrom(null));
console.log(booksFrom('[{"title":"Dune"}]'));
console.log(booksFrom("[{title:Dune}"));
[]
[ { title: 'Dune' } ]
[]

Try it. In your reading-list page's app.js, save a small array of two books with localStorage.setItem and JSON.stringify. Find it in the developer tools' Application (or Storage) tab, then reload the page and read it back in the Console with JSON.parse(localStorage.getItem("reading-list")). Last, delete the entry in the developer tools and run the same line again: null.

sessionStorage and cookies

sessionStorage has exactly the same methods as localStorage, setItem, getItem and removeItem, but it's tied to one tab: it survives a reload, and it's gone when the tab closes. It suits things that belong to one sitting, such as the answers to a long form someone is halfway through, or a game round in progress.

Cookies are older and different. A cookie is a small piece of text that the browser sends back to the server with every request to that site. That's what makes them useful for staying signed in: the server sets a cookie when you sign in, and every page you open after that carries it, so the server knows it's you. Servers usually set and read them, and page code rarely needs to touch them, which is why this course doesn't.

Keeps it until… Sent to the server? Good for
localStorage your code or the person removes it, or, in Safari, after seven days with no visit no a reading list, settings, favorites, a high score
sessionStorage the tab closes no a half-filled form, a game round
Cookie the date the server gives it yes, with every request staying signed in

What never to store

localStorage isn't locked. Any JavaScript running on your page can read every key in it, including code from another site that the page loads, and anyone sitting at that computer can open the developer tools and read it. It isn't encrypted either. So:

  • Never store passwords, credit card numbers, or private details such as a home address or health information.
  • Do store things that would be fine on a sticky note on the person's own screen: their reading list, their dark-mode choice, which photos they marked as favorites.

There's a size limit too, about 5 MB per site in most browsers: plenty for text, too little for photos.

Check yourself

2 questions to try on the lesson page.

So what

With JSON.stringify, JSON.parse and a sensible empty start, any page can remember a person's things between visits, with no server and no account. Settings, favorites, high scores and drafts are all built this way.

Key terms

Cheat sheets

Related