Free lesson · JavaScript Basic to Advance
Storage, Timers, and Other Browser APIs
The Useful Browser APIs Beyond DOM
The browser provides many APIs beyond the DOM. This Topic covers the most commonly used: storage APIs for persisting data, timer APIs for scheduling work, and a few other utilities that come up regularly.
// Storage
localStorage.setItem("preference", "dark");
const pref = localStorage.getItem("preference");
// Timers
setTimeout(() => console.log("after 1 second"), 1000);
const id = setInterval(() => tick(), 1000);
clearInterval(id);
// Animation
requestAnimationFrame(updateFrame);
// URL parsing
const url = new URL("https://example.com/path?query=value");
console.log(url.pathname, url.searchParams.get("query"));
These APIs are well-supported across modern browsers. Each has specific use cases and trade-offs worth knowing.
localStorage and sessionStorage
The Web Storage API gives you two persistent key-value stores in the browser.
localStorage — persists across browser restarts. The same domain reads the same data across tabs and sessions. Cleared only when the user explicitly clears site data.
sessionStorage — persists only for the current browser tab. Cleared when the tab is closed.
Both APIs are identical except for persistence:
// Store a value
localStorage.setItem("username", "olena");
// Read a value
const username = localStorage.getItem("username");
// Remove a value
localStorage.removeItem("username");
// Clear all values for this domain
localStorage.clear();
// Iterate
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
console.log(key, value);
}
Key things to know:
Only strings. Both keys and values must be strings. To store objects or arrays, serialize with JSON.stringify and parse with JSON.parse:
// Save
const settings = { theme: "dark", fontSize: 14 };
localStorage.setItem("settings", JSON.stringify(settings));
// Load
const saved = JSON.parse(localStorage.getItem("settings"));
Storage is per-origin. Different domains, subdomains, or protocols (HTTP vs HTTPS) have separate storage. Code on example.com can't read storage written by code on api.example.com.
Limited size. Most browsers cap storage at 5-10MB per origin. Trying to store more throws a QuotaExceededError.
Synchronous. Read and write are blocking. For very small data this is fine. For large data, it can briefly block the main thread.
Not encrypted. Anything stored is visible in DevTools and accessible to any script running on the same origin. Don't store sensitive data like passwords or tokens directly.
For most "remember the user's preferences" use cases, localStorage is the right tool. For "remember state during this tab" cases, sessionStorage. For larger or more structured data, IndexedDB is the more capable alternative — it's an async, schema-supporting database in the browser. IndexedDB has a steeper API than localStorage and is beyond this Topic's scope.
Interactive animation: Strings only - localStorage keeps an object intact only through the JSON.stringify and JSON.parse round trip.
Cookies vs Storage
Cookies are the older mechanism for storing data, but they have specific characteristics:
- Sent with every HTTP request. Cookies are automatically included in requests to the same origin. Storage isn't sent unless you read it and add it to the request.
- Server-set. Often set by the server via HTTP headers, not just by JavaScript.
- Smaller limits. Cookies are typically limited to about 4KB each.
- Can be HttpOnly. Cookies marked
HttpOnlyare inaccessible to JavaScript — useful for security-sensitive tokens.
Use cookies when the server needs to see the data automatically. Use localStorage when only the client needs it. For most modern apps with token-based auth, you'll see a mix — HttpOnly cookies for session tokens, localStorage for UI preferences.
Interactive animation: The storage decision table - HttpOnly cookies for session tokens, localStorage for preferences, sessionStorage per tab, IndexedDB for more.
Timers: setTimeout, setInterval, requestAnimationFrame
JavaScript has three main timer APIs.
setTimeout(callback, ms) — runs callback once after at least ms milliseconds.
const id = setTimeout(() => console.log("delayed"), 1000);
clearTimeout(id); // cancel before it fires
setInterval(callback, ms) — runs callback repeatedly every ms milliseconds.
const id = setInterval(() => console.log("tick"), 1000);
clearInterval(id); // stop the interval
requestAnimationFrame(callback) — runs callback before the next browser paint, typically ~16ms (60fps) but adjusted for screen refresh rate and system load.
function animate() {
// update something visual
requestAnimationFrame(animate); // schedule next frame
}
requestAnimationFrame(animate);
The key differences:
| Aspect | setTimeout / setInterval | requestAnimationFrame |
|---|---|---|
| Timing | Specified delay (ms) | Synchronized to browser paint |
| Tab in background | Continues firing | Pauses (saves battery) |
| For animation | Imprecise | Designed for animation |
| For non-visual work | Good | Wrong tool |
For animation, always use requestAnimationFrame. It synchronizes with the browser's rendering, runs at the right time in the frame cycle, and automatically pauses when the tab is hidden.
For non-animation periodic work, setTimeout or setInterval are appropriate. A common pattern is to use setTimeout recursively instead of setInterval for more control:
function poll() {
doWork();
setTimeout(poll, 1000); // schedule next call after this one completes
}
setTimeout(poll, 1000);
This pattern avoids overlapping invocations if doWork() itself sometimes runs longer than 1000ms. setInterval would queue invocations regardless of whether the previous one finished.
Interactive animation: Guessing the frame vs riding it - a setInterval animation beside requestAnimationFrame, which runs before each paint.
The URL API
The URL constructor parses and manipulates URLs:
const url = new URL("https://example.com/path/to/page?q=hello&page=2#section");
url.protocol // "https:"
url.host // "example.com"
url.hostname // "example.com"
url.port // "" (empty if default)
url.pathname // "/path/to/page"
url.search // "?q=hello&page=2"
url.hash // "#section"
// Working with query parameters
url.searchParams.get("q"); // "hello"
url.searchParams.get("page"); // "2"
url.searchParams.set("page", "3");
console.log(url.toString()); // updated URL with page=3
Before the URL API, developers parsed URLs with regex or string splitting. URL is more reliable and handles edge cases (encoding, default ports, relative URLs) correctly.
window.location
The current page's URL is exposed as window.location:
window.location.href // full URL of current page
window.location.protocol // "https:"
window.location.host // "example.com"
window.location.pathname // "/current/path"
window.location.search // "?param=value"
window.location.hash // "#section"
// Navigation
window.location.href = "/new-page"; // navigate to new URL
window.location.reload(); // reload current page
window.location.replace("/new-page"); // navigate without adding to history
For single-page apps that handle their own routing, the history API is what manages URL changes without full page reloads:
// Push a new URL without reloading
history.pushState(state, "", "/new-route");
// Replace current entry instead of pushing
history.replaceState(state, "", "/replaced-route");
// Listen for back/forward navigation
window.addEventListener("popstate", (event) => {
console.log("Navigated to", location.pathname);
});
Modern routing libraries (React Router, Vue Router, etc.) wrap this API to provide a more developer-friendly interface, but it's good to know what's underneath.
A Few Others to Be Aware Of
navigator — info about the browser and environment. navigator.userAgent, navigator.language, navigator.onLine (whether the browser thinks it's online), navigator.clipboard (read/write the clipboard).
crypto — cryptographic operations. crypto.randomUUID() generates random UUIDs. crypto.getRandomValues() produces cryptographically random numbers. The Subtle Crypto API does hashing, encryption, signing.
performance — high-resolution timing for performance measurement. performance.now() returns sub-millisecond timestamps.
IntersectionObserver — observes when elements enter or leave the viewport. Better than scroll event handlers for "is this element visible" use cases.
MutationObserver — observes DOM changes. Useful for code that needs to react to DOM modifications it doesn't directly control.
ResizeObserver — observes when elements change size. Useful for responsive components that need to react to size changes.
The observers (Intersection, Mutation, Resize) are particularly worth knowing — they replace older patterns that used scroll/resize event handlers and were performance problems.
Common Pitfalls
Warning: The browser API traps.
Storing complex data in localStorage as strings. Always JSON-serialize objects and arrays. Forgetting to parse back to objects is a source of "undefined property" bugs.
Using setInterval for animations. Use
requestAnimationFrame— synchronized to the browser, pauses in background tabs, better performance.Using setInterval for periodic work without checking how long each iteration takes. Recursive setTimeout is safer.
Storing tokens in localStorage when HttpOnly cookies would be safer. Anything in localStorage is accessible to any script on your domain, including malicious ones injected via XSS.
Why This Matters in Interviews
Interview: Browser API fluency is foundational for frontend interviews. Knowing the storage trade-offs, the timer differences, and the URL/location APIs is expected at all levels. The observer APIs (IntersectionObserver in particular) come up in performance-focused interviews.
Topic 12.7 — Interview Lens: Browser and DOM Questions — covers the deeper treatment.
Try It
Exercise: Build a small "saved preferences" component:
- A form with three controls: a theme selector (light/dark), a font-size slider, and a "save on close" checkbox
- On any change, save the current state to
localStorageas a JSON-encoded object- On page load, read from
localStorageand apply the saved values- Handle the case where there are no saved values (first visit)
Once this works, you've exercised localStorage with structured data — the standard pattern for client-side preferences.