localStorage, Cookies & IndexedDB
Advanced · 20 min read · ▶ live playground · ✦ checkpoint
Browser apps remember things in a few different places. localStorage, cookies, and IndexedDB all store data, but they solve different problems - and choosing the wrong one can make your UI slow, leaky, or impossible to run in restricted browser contexts.
The beginner question is usually "where do I save this?" The professional answer starts with "who needs to read it, how much is it, and what happens when storage is unavailable?"
Small storage: localStorage and sessionStorage
Web Storage is the browser feature behind localStorage and sessionStorage. Both APIs store key-value pairs where the keys are strings and the values are strings.
The difference is lifetime:
localStorageis for small preferences that should survive later visits in a regular same-origin page: theme, dismissed banner IDs, compact-table preference.sessionStorageis for small values tied to one tab session: an unfinished wizard step, a temporary filter, a checkout draft you do not want shared with another tab.
The everyday API shape is simple:
const profile = { name: "Mina", theme: "dark" };
localStorage.setItem("profile", JSON.stringify(profile));
const savedText = localStorage.getItem("profile");
const savedProfile = savedText === null ? null : JSON.parse(savedText);The JSON.stringify and JSON.parse calls are not decoration. Web Storage stores strings only:
const profile = { name: "Mina", theme: "dark" };
console.log(String(profile));
const savedText = JSON.stringify(profile);
console.log(savedText);
const restored = JSON.parse(savedText);
console.log(restored.name);String(profile) produces the famous wrong value: [object Object]. JSON turns the object into useful text, then turns it back into an object later.
There is another trap: storage can be unavailable. Private modes, browser settings, embedded pages, and sandboxed frames can block these APIs. In this course's live page preview, the iframe is intentionally sandboxed without same-origin access, so reading localStorage throws in Chromium:
SecurityError: Failed to read the 'localStorage' property from 'Window': The document is sandboxed and lacks the 'allow-same-origin' flag.
Browser wording can vary, but the rule does not: storage access belongs inside a small try/catch wrapper, not scattered through the app.
Try it - detect storage and fall back
This live demo does not pretend the preview can persist browser storage. It checks the APIs, catches the security errors, then uses in-memory state for the current Run. That is the honest shape you want in restricted contexts: detect capability, render the current truth, and keep the UI usable.
That demo is not a storage replacement. It is a defensive pattern: storage is an optional capability, not a law of physics. When saving matters, keep the capability check near the storage adapter so the rest of your UI can respond with a clear state.
Also notice the word "small." Web Storage is synchronous, so a big localStorage.getItem(...) or setItem(...) blocks the page's main thread while it runs. Use it for tiny preferences, not large documents, search indexes, or thousands of records.
Cookies are made for the server
Cookies are small pieces of text the browser stores for a site and automatically sends back on matching HTTP requests. That automatic send is the whole reason cookies exist: the server can recognize a session, a locale choice, or a small experiment assignment when the next request arrives.
A server-set cookie usually looks like this:
Set-Cookie: session=abc123; Path=/; Max-Age=3600; Secure; HttpOnly; SameSite=LaxThe flags matter:
Securemeans send the cookie only over HTTPS.HttpOnlymeans JavaScript cannot read it withdocument.cookie; the server controls it. This is the normal choice for session cookies.SameSitelimits when the browser sends the cookie with cross-site requests.Laxis a common default;Strictis tighter;Noneis for explicitly cross-site use and requiresSecure.ExpiresorMax-Agedecides when the cookie dies.PathandDomaindecide which URLs receive it.
JavaScript can create simple non-HttpOnly cookies in a regular page:
document.cookie = "theme=dark; Max-Age=2592000; Path=/; SameSite=Lax; Secure";But the browser sends cookies on requests whether your JavaScript is thinking about them or not. That makes them a poor place for bulky UI state. If the server does not need the value on every request, a cookie is usually the wrong store.
IndexedDB is a real browser database
IndexedDB is the browser's asynchronous database for larger, structured data. Instead of string-only key-value pairs, it stores JavaScript values in object stores, reads and writes through transactions, and can create indexes for efficient lookup.
The shape is more verbose because databases need setup:
const openRequest = indexedDB.open("coach-app", 1);
openRequest.addEventListener("upgradeneeded", () => {
const db = openRequest.result;
db.createObjectStore("sessions", { keyPath: "id" });
});
openRequest.addEventListener("success", () => {
const db = openRequest.result;
const tx = db.transaction("sessions", "readwrite");
const store = tx.objectStore("sessions");
store.put({
id: "session-1",
student: "Rae",
notes: "Review closures before the next call.",
});
});Use IndexedDB when the browser needs a real local dataset: offline-first notes, cached API responses, draft documents, media metadata, or many records that must be searched without asking the server every time. Do not reach for it just to remember "dark mode."
In the sandboxed preview, indexedDB.open(...) is denied too; Chromium reports:
SecurityError: Failed to execute 'open' on 'IDBFactory': access to the Indexed Database API is denied in this context.
Again, browser wording can vary. The important design move is the same as Web Storage: isolate the storage code, catch failures, and give the UI a usable fallback state.
Choose by the job
Use the store that matches the data's job:
- Memory for current screen state you can rebuild.
- URL for state the user should be able to share or bookmark; lesson 17.3 owns that shape.
sessionStoragefor tiny per-tab values.localStoragefor tiny same-browser preferences that are safe to keep as strings.- Cookies for values the server needs on requests, especially server-set session cookies with
Secure,HttpOnly, andSameSite. - IndexedDB for larger structured browser datasets and offline work.
Storage is not state architecture by itself. It is only where some values live between moments. Next, you will build the loop that makes browser UIs feel organized: event, update one state object, render the page from that state.
Checkpoint
Answer all three to mark this lesson complete