14 minLesson 20 of 35
DOM & Browser APIs
Local Storage & Session Storage
Local Storage & Session Storage
Browser storage lets you persist data on the user's device without a server. It's used for user preferences, cart contents, auth tokens, cached data, and offline functionality.
The Two Options
| Feature | localStorage | sessionStorage |
|---|---|---|
| Lifetime | Until cleared | Until tab closes |
| Scope | All tabs, same origin | Current tab only |
| Limit | ~5-10MB | ~5-10MB |
| Shared across tabs | Yes | No |
Both have identical APIs. The choice depends on how long you need the data.
Core API
// Store a value
localStorage.setItem("theme", "dark");
localStorage.setItem("language", "en");
// Read a value
const theme = localStorage.getItem("theme"); // "dark"
const missing = localStorage.getItem("xyz"); // null (not undefined)
// Remove one item
localStorage.removeItem("theme");
// Clear everything
localStorage.clear();
// Number of stored items
localStorage.length; // 1
// Iterate all keys
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
console.log(key, value);
}
Storing Objects
localStorage only stores strings. JSON handles serialization:
// Store an object
const user = { id: 1, name: "Alice", role: "admin" };
localStorage.setItem("user", JSON.stringify(user));
// Retrieve it
const stored = localStorage.getItem("user");
const user = stored ? JSON.parse(stored) : null;
// Helper utility (recommended pattern)
const storage = {
get(key, defaultValue = null) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch {
return defaultValue;
}
},
set(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
remove(key) {
localStorage.removeItem(key);
},
clear() {
localStorage.clear();
}
};
// Usage
storage.set("cart", [{ id: 1, qty: 2 }, { id: 3, qty: 1 }]);
const cart = storage.get("cart", []);
Practical Use Cases
User Preferences
const PREFS_KEY = "user_preferences";
const defaultPrefs = {
theme: "light",
language: "en",
fontSize: 16,
notifications: true
};
function getPrefs() {
return { ...defaultPrefs, ...storage.get(PREFS_KEY, {}) };
}
function savePrefs(updates) {
const current = getPrefs();
storage.set(PREFS_KEY, { ...current, ...updates });
applyPrefs();
}
function applyPrefs() {
const { theme, fontSize } = getPrefs();
document.documentElement.setAttribute("data-theme", theme);
document.documentElement.style.fontSize = `${fontSize}px`;
}
// Apply on load
applyPrefs();
// Toggle theme
document.querySelector("#theme-toggle").addEventListener("click", () => {
const { theme } = getPrefs();
savePrefs({ theme: theme === "light" ? "dark" : "light" });
});
Shopping Cart
const CART_KEY = "shopping_cart";
const cart = {
getItems() {
return storage.get(CART_KEY, []);
},
addItem(product) {
const items = this.getItems();
const existing = items.find(i => i.id === product.id);
if (existing) {
existing.qty += 1;
} else {
items.push({ ...product, qty: 1 });
}
storage.set(CART_KEY, items);
this.updateUI();
},
removeItem(productId) {
const items = this.getItems().filter(i => i.id !== productId);
storage.set(CART_KEY, items);
this.updateUI();
},
getTotal() {
return this.getItems().reduce((sum, i) => sum + i.price * i.qty, 0);
},
updateUI() {
const count = this.getItems().reduce((n, i) => n + i.qty, 0);
document.querySelector(".cart-count").textContent = count;
}
};
Storage Events
When localStorage changes in another tab, a storage event fires:
window.addEventListener("storage", (event) => {
console.log(event.key); // which key changed
console.log(event.oldValue); // previous value (string)
console.log(event.newValue); // new value (string)
console.log(event.storageArea); // localStorage or sessionStorage
// Sync state across tabs
if (event.key === "user") {
const user = JSON.parse(event.newValue);
updateUserUI(user);
}
if (event.key === "user" && event.newValue === null) {
// User logged out in another tab
redirectToLogin();
}
});
When NOT to Use localStorage
// ❌ Never store sensitive data here
localStorage.setItem("password", password); // readable by any JS on your domain
localStorage.setItem("credit_card", number); // XSS would steal it
// ✅ Auth tokens: use httpOnly cookies instead
// They're inaccessible to JavaScript and safe from XSS
// ❌ Don't store large data (videos, images)
// Use IndexedDB for anything over a few MB
// ❌ Don't treat it as reliable — users can clear it
// Always have a server-side source of truth
sessionStorage Use Cases
// Multi-step form — keep data until user finishes or closes tab
function saveFormProgress(step, data) {
const saved = JSON.parse(sessionStorage.getItem("form_progress") || "{}");
saved[step] = data;
sessionStorage.setItem("form_progress", JSON.stringify(saved));
}
function getFormProgress(step) {
const saved = JSON.parse(sessionStorage.getItem("form_progress") || "{}");
return saved[step] || null;
}
// Scroll position restoration on back navigation
window.addEventListener("beforeunload", () => {
sessionStorage.setItem("scrollY", window.scrollY.toString());
});
const savedScroll = parseInt(sessionStorage.getItem("scrollY") || "0");
window.scrollTo(0, savedScroll);
Storage Limits
// Check available space
function getStorageSize() {
let total = 0;
for (const key of Object.keys(localStorage)) {
total += localStorage.getItem(key).length * 2; // UTF-16 = 2 bytes per char
}
return `${(total / 1024).toFixed(2)} KB`;
}
// Handle quota exceeded
try {
localStorage.setItem("large", bigData);
} catch (e) {
if (e.name === "QuotaExceededError") {
// Clear old data, then retry
localStorage.removeItem("cache");
localStorage.setItem("large", bigData);
}
}
Next lesson: The Event Loop & Call Stack — understanding JavaScript's concurrency model.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises