// apis · Web Platform Advent #6
localStorage vs sessionStorage: the Web Storage API explained
When to use localStorage vs sessionStorage — how they differ in lifetime and scope, their size limits, how to store JSON safely, and reacting to the storage event.
The Web Storage API gives the browser two simple key-value stores — localStorage and sessionStorage — for keeping small amounts of data on the user's device. Both share the exact same API; they differ only in how long the data lives and who can see it.
The shared API
Both objects expose the same five methods, and both store strings only:
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme'); // 'dark'
localStorage.removeItem('theme');
localStorage.clear(); // wipe everything
console.log(localStorage.length); // number of keys Reading a key that does not exist returns null, so you can fall back with ||:
const theme = localStorage.getItem('theme') || 'light'; The one real difference: lifetime
This is the whole decision in one sentence. localStorage persists until you delete it — it survives reloads, tab closes and browser restarts. sessionStorage lives only as long as the tab does: close the tab and the data is gone, and a second tab on the same site gets its own separate store.
window object, so localStorage and window.localStorage refer to exactly the same thing.Storing objects: stringify and parse
Because storage holds only strings, anything structured must go through JSON. Serialize on the way in, parse on the way out:
const user = { name: 'Ada', loggedIn: true };
// Save
localStorage.setItem('user', JSON.stringify(user));
// Read back
const saved = JSON.parse(localStorage.getItem('user'));
console.log(saved.name); // 'Ada' Wrap JSON.parse in a try/catch when the value might be missing or corrupted — parsing null or invalid text throws:
function readJSON(key, fallback) {
try {
const raw = localStorage.getItem(key);
return raw ? JSON.parse(raw) : fallback;
} catch {
return fallback;
}
} Scope and the size limit
Storage is scoped to the origin (scheme + host + port): https://example.com and http://example.com do not share data. The quota is roughly 5 MB per origin in most browsers — generous for preferences and small caches, but not a database. Exceeding it makes setItem throw a QuotaExceededError, so guard large writes.
Reacting to changes across tabs
The storage event fires on other tabs of the same origin when localStorage changes — handy for syncing a logout or a theme switch across open windows. Note it does not fire in the tab that made the change, and sessionStorage never triggers it:
window.addEventListener('storage', (event) => {
if (event.key === 'theme') {
applyTheme(event.newValue);
}
}); Which one should you use?
| localStorage | sessionStorage | |
|---|---|---|
| Lifetime | Until explicitly cleared | Until the tab closes |
| Shared between tabs | Yes (same origin) | No (per tab) |
| Survives reload | Yes | Yes |
Fires storage event | Yes | No |
| Typical use | Theme, settings, "remember me" | One-off form drafts, wizard steps |
Reach for localStorage when the data should stick around between visits, and sessionStorage when it is meaningful only for the current tab. For anything sensitive — tokens, passwords, personal data — neither is appropriate: both are readable by any script on the page, so keep secrets out of Web Storage entirely.