Level 4, Real Data & Architecture, Chapter 17 of 24
Browser Storage & Web APIs
Why this matters
Refresh a page and, normally, every JavaScript variable you had disappears. But plenty of sites remember you: your shopping cart is still full, your dark mode preference sticks around, your draft comment is still there even if you accidentally closed the tab. This persistence isn't magic, it's the browser's storage APIs quietly saving small amounts of data on your device.
Learning browser storage means you can finally build features that survive a refresh, which is often the difference between a demo and something that feels like a real product. Alongside storage, you'll also see a glimpse of the many other Web APIs the browser exposes beyond the DOM, from geolocation to clipboard access, all following the same JavaScript-object style you already know.
The lesson
localStorage and sessionStorage
localStorage and sessionStorage are simple key-value stores built into every browser. Both use the same API: setItem(key, value), getItem(key), removeItem(key), and clear(). The difference is lifespan: localStorage persists indefinitely, until the user or your code clears it, even after closing the browser entirely, while sessionStorage is wiped as soon as the tab is closed. Both only store strings, so to save an object or array you must convert it with JSON.stringify() before saving, and convert it back with JSON.parse() when you read it out.
1const preferences = { theme: "dark", fontSize: 16 };23localStorage.setItem("preferences", JSON.stringify(preferences));45const saved = JSON.parse(localStorage.getItem("preferences"));6console.log(saved.theme); // "dark"A safe pattern is to always guard your reads: localStorage.getItem returns null if the key doesn't exist yet, and calling JSON.parse(null) actually returns null rather than throwing, but it's clearer to check explicitly and provide a sensible default. This matters the first time a user visits your page and there's nothing saved yet. It's also worth remembering that storage is per-origin, meaning localStorage on example.com is completely separate from localStorage on another-site.com, and that anything you store there is readable by anyone with access to that browser, so it should never hold sensitive information like passwords.
1function loadJSON(key, fallback) {2 const raw = localStorage.getItem(key);3 if (!raw) return fallback;4 try {5 return JSON.parse(raw);6 } catch {7 return fallback;8 }9}1011const cart = loadJSON("cart", []);Persisting UI state across reloads
A very common use of storage is remembering a user's preference, like light or dark mode, or the last tab they had open. The pattern is always the same: read from storage when the page loads and apply that state immediately, then write to storage whenever the state changes, usually inside the same event handler that updates the DOM. This combines skills from earlier chapters directly: the event listeners you already know for reacting to clicks, and the DOM manipulation you already know for applying classes, now paired with storage so the change survives a reload instead of resetting every time.
Other Web APIs worth knowing
localStorage is just one of many APIs the browser exposes on the global window and navigator objects, all designed to feel like ordinary JavaScript. The Geolocation API (navigator.geolocation.getCurrentPosition) asks the user for permission and returns their coordinates. The Clipboard API (navigator.clipboard.writeText) lets you copy text programmatically, commonly used for 'copy link' buttons. The Notifications API can show system notifications with permission. Most of these follow a similar shape: they're asynchronous (often Promise-based), and they typically require the user to grant permission first, which the browser handles with a built-in prompt.
localStoragepersists until explicitly cleared, across browser restartssessionStorageclears when the tab closes- Both APIs only store strings, so use JSON.stringify/JSON.parse for objects
- Never store sensitive data like passwords in browser storage
- Storage is separate per origin (domain)
- Many Web APIs (geolocation, clipboard) require user permission and are Promise-based
It's tempting to reach for storage for everything, but it has real limits worth knowing: it's synchronous (which can block the main thread if you store large amounts of data), it's capped at roughly five megabytes per origin in most browsers, and it isn't designed for structured queries. For genuinely large or complex client-side data, browsers offer IndexedDB, a more powerful asynchronous database, but for preferences, small caches, and simple persistence, localStorage is usually the right, simple tool for the job.
Try it yourself
This notes app saves to localStorage, so your notes survive a page reload. Add a note, then reload the sandbox to see it persist.
Chapter boss project
Build a Preferences-Aware Dashboard
Build a small settings panel with at least three preferences the user can change: a theme (light/dark), a display name, and one more of your choosing (font size, layout density, whatever you like). Every preference should be saved to localStorage the moment it changes.
When the page loads, it should read saved preferences and apply them immediately, before the user interacts with anything, so a reload feels seamless rather than resetting to defaults.
Add a 'Reset to defaults' button that clears the saved preferences and returns the UI to its original state, demonstrating that you understand both writing and removing data from storage.
- Store all preferences together as a single object under one key, rather than many separate keys, to keep reading and writing simpler.
- Write one `applyPreferences(prefs)` function that updates the DOM, and call it both on page load and whenever a setting changes.
- Use your loadJSON-with-default pattern so the very first visit (with nothing saved yet) doesn't break anything.
- For reset, call localStorage.removeItem and then call applyPreferences with your hardcoded defaults.
Level-up checklist
Tick these off once each one is true for you.
