Q1What happens when you run localStorage.getItem("theme") with a key that has never been saved?
localStorage — Saving Data in the Browser
Save values in the browser with localStorage's setItem, getItem, and removeItem, store arrays as JSON strings, and handle null and the size limit.
If you keep a user's cart items or display settings in variables, they're lost when the page is reloaded or closed, and everything goes back to its initial state the next time it opens. None of the code so far has had a place to keep values across page loads.
This article covers localStorage, which saves values in the browser, and the JSON strings you use to save arrays and objects.
Keeping Values After the Page Closes — setItem and getItem
Suppose a news site wants to remember the font size a user chose and use it again on their next visit. Even if you put it in a variable, as in const fontSize = "large";, variables are lost when the page is reloaded or closed, so the next page load can't read it.
localStorage (a key-value store that the browser provides) saves a value with setItem(key, value), reads it back with getItem(key), and removes it with removeItem(key).
Storage is separated by origin (the part of a URL before the path — https://news.example in https://news.example/settings), and pages from the same origin read and write the same values.
// If a value from before is still saved, remove it so we start with nothing saved
localStorage.removeItem("fontSize");
// Save when the user switches the font size to large
localStorage.setItem("fontSize", "large");
// When the page opens, read the saved value and use it
const fontSize = localStorage.getItem("fontSize");
console.log(fontSize); // large
// Saving under the same key replaces the previous value
localStorage.setItem("fontSize", "small");
console.log(localStorage.getItem("fontSize")); // small
- Pages and tabs from this origin share one localStorage
"fontSize"→"small"(replaced by saving to the same key)- Stays after the page is closed or reloaded
- Variables like
const fontSize - Variables are re-created on reload
- Has its own localStorage
- Can't read the
"fontSize"saved by news.example
If you read with getItem when the page opens and write with setItem when the user changes the setting, the page shows the same font size even after a reload. Saved values stay until the page's code removes them or the user clears them in the browser settings.
Saved Values Carry Over Between Exercise Runs
The exercise console runs every JavaScript exercise on this site on the same origin, so they all share one localStorage, and its contents stay even if you reopen the page. To keep values saved in earlier runs from mixing into your results, the exercises start by removing the keys they use with removeItem.
Saving Arrays and Objects — JSON.stringify
A travel booking site keeps the hotels a user has added to their favorites in an array and wants to list them again on the next visit. Passing the array straight to setItem doesn't cause an error, but the value you read back contains neither the hotel IDs nor their names.
setItem converts the value you pass to a string before saving it. Turn arrays and objects into JSON strings with JSON.stringify before saving them, and turn the string you read back into the original array or object with JSON.parse.
// If a value from before is still saved, remove it first
localStorage.removeItem("favoriteHotels");
const favoriteHotels = [{ id: "H-101", name: "Harbor Hotel" }, { id: "H-204", name: "Station Inn" }];
// Passed directly, the array is converted to a string when it's saved
localStorage.setItem("favoriteHotels", favoriteHotels);
console.log(localStorage.getItem("favoriteHotels")); // [object Object],[object Object]
// Convert it to a JSON string before saving
localStorage.setItem("favoriteHotels", JSON.stringify(favoriteHotels));
const saved = localStorage.getItem("favoriteHotels");
console.log(saved); // [{"id":"H-101","name":"Harbor Hotel"},{"id":"H-204","name":"Station Inn"}]
// Turn the string you read back into an array before using it
const restored = JSON.parse(saved);
console.log(restored[1].name); // Station Inn
Numbers and booleans are converted silently too, with no error, so you only notice when a calculation or an if check on the value you read back goes wrong. The table below shows, for each value passed to setItem, the string that's saved and what happens when you read it back.
| Value Passed to setItem | Stored String | When You Read It Back |
|---|---|---|
| The number 20 | "20" | "20" + 1 is "201" (use Number) |
| The boolean false | "false" | Truthy in if (use JSON.parse) |
| String array ["A-1", "B-2"] | "A-1,B-2" | One comma-joined string, not an array |
Reading a Key That Isn't Saved — the null from getItem
On a recipe site, the IDs of viewed recipes are saved as a history, and the page shows how many there are when it opens. First-time visitors and people who cleared their history have nothing saved, so if you read the length of the JSON.parse result assuming a history exists, that line throws a TypeError.
When you pass getItem a key that isn't saved, it returns null, not undefined. If you write JSON.parse(localStorage.getItem(key)) ?? [], an empty array is used only when nothing is saved.
// Read the view history. If nothing is saved, use an empty array
function loadHistory() {
return JSON.parse(localStorage.getItem("viewHistory")) ?? [];
}
// First visit — nothing is saved, so getItem returns null
localStorage.removeItem("viewHistory");
console.log(localStorage.getItem("viewHistory")); // null
console.log(loadHistory().length); // 0
// With a saved history — the string is turned back into an array
localStorage.setItem("viewHistory", JSON.stringify(["R-12", "R-30"]));
console.log(loadHistory().length); // 2
// After removeItem — it's null again
localStorage.removeItem("viewHistory");
console.log(loadHistory().length); // 0
If you put the reading code in a function, callers can treat the result as an array without checking for null, even after removeItem is called on logout. removeItem doesn't throw an error even for a key that isn't saved, so you don't need to check whether something is saved before removing it.
Restoring the Cart After a Reload — Save and Load Functions
Next, an online store's cart should keep its contents when the page is reloaded after an item is added. Adding an item with cart.push only changes the variable, so if you miss the save line anywhere, that item disappears on reload.
Put saving in a saveCart function and loading in a loadCart function, and call them right after the cart changes and when the page opens. setItem is synchronous (it finishes saving before moving on to the next line), so unlike fetch, you can read the saved value on the next line without await.
localStorage.removeItem("cart"); // If a cart from before is still saved, remove it
// Save the cart as a JSON string. No await needed
function saveCart(cart) {
localStorage.setItem("cart", JSON.stringify(cart));
}
// Turn the saved cart back into an array. If nothing is saved, return an empty cart
function loadCart() {
return JSON.parse(localStorage.getItem("cart")) ?? [];
}
// Load when the page opens, and save after adding an item
const cart = loadCart();
cart.push({ id: "K-310", qty: 1 });
saveCart(cart);
// Instead of reloading, load again from the saved string without using the cart variable
const reloaded = loadCart();
console.log(reloaded.length, reloaded[0].id); // 1 K-310
If you put everything that changes the cart in one function and call saveCart at its end, you don't have to write the save at every call site, and you can't forget it. Loading is also wrapped in loadCart, so there's no place where you could forget ?? [] either.
setItem Throws an Exception When Storage Is Full
In Chrome, one origin can store about 5.24 million characters, counting keys and values together. Beyond that, setItem throws a QuotaExceededError and the value isn't saved. Reads and writes are synchronous, so the page also stops responding while a large value is being saved. Use it for small values like carts and settings.
Knowledge Check
Answer each question one by one.
Q2After localStorage.setItem("darkMode", false), what happens if you use the value you read back as an if condition?
Q3If you reload after cart.push(book) without calling saveCart, what does loadCart() return?