Web Storage
EasyWeb Storage provides two mechanisms for storing key-value data in the browser: localStorage (persists until cleared) and sessionStorage (cleared when the tab closes). Both store strings only, so objects must be serialized with JSON.stringify. The storage event enables cross-tab communication.
Interactive Visualization
DOM Events
document>
body>
div>
button
Phase: Capture
Event travels DOWN from document to target
Key Points
- localStorage persists across sessions; sessionStorage clears when the tab closes
- Both store strings only: use JSON.stringify/JSON.parse for objects and arrays
- API: setItem(key, value), getItem(key), removeItem(key), clear(), key(index)
- Storage limit is roughly 5MB per origin (varies by browser)
- The storage event fires on OTHER tabs when localStorage changes (cross-tab sync)
- Never store sensitive data (tokens, passwords) in Web Storage: vulnerable to XSS
Code Examples
localStorage Basics
localStorage.setItem('username', 'alice'); console.log(localStorage.getItem('username')); // 'alice' console.log(localStorage.getItem('missing')); // null // Store objects (must stringify) const settings = { fontSize: 16, lang: 'en' }; localStorage.setItem('settings', JSON.stringify(settings)); const saved = JSON.parse(localStorage.getItem('settings')); localStorage.removeItem('username'); localStorage.clear();
localStorage persists until explicitly cleared. All values are strings, objects need JSON.
sessionStorage vs localStorage
// sessionStorage: lives only for the tab session sessionStorage.setItem('formDraft', 'Hello world...'); // Survives page refreshes, disappears on tab close // Use case: multi-step form wizard function saveFormStep(step, data) { const state = JSON.parse(sessionStorage.getItem('wizard') || '{}'); state[`step${step}`] = data; sessionStorage.setItem('wizard', JSON.stringify(state)); }
sessionStorage is tab-scoped and temporary. localStorage is origin-scoped and permanent.
Cross-Tab Communication
// The 'storage' event fires in OTHER tabs window.addEventListener('storage', (event) => { console.log('Key changed:', event.key); console.log('New value:', event.newValue); if (event.key === 'logout') { window.location.href = '/login'; } if (event.key === 'theme') { document.body.className = event.newValue; } }); // Tab A triggers event in Tab B localStorage.setItem('theme', 'dark');
The storage event fires only in OTHER tabs of the same origin when localStorage changes.
Storage Wrapper with TTL
function storageAvailable(type) { try { const storage = window[type]; storage.setItem('__test__', 'test'); storage.removeItem('__test__'); return true; } catch { return false } } const storage = { set(key, value, ttlMs) { const item = { value, expiry: ttlMs ? Date.now() + ttlMs : null }; localStorage.setItem(key, JSON.stringify(item)); }, get(key) { const raw = localStorage.getItem(key); if (!raw) return null; const item = JSON.parse(raw); if (item.expiry && Date.now() > item.expiry) { localStorage.removeItem(key); return null; } return item.value; }, }; storage.set('token', 'abc', 60 * 60 * 1000); // 1 hour TTL
Check availability for private browsing. A wrapper adds expiry support since Web Storage has no TTL.
Common Mistakes
- Storing objects without JSON.stringify (you get "[object Object]")
- Not wrapping in try-catch (throws in private browsing mode)
- Storing sensitive data like auth tokens (vulnerable to XSS)
- Expecting the storage event to fire in the same tab
Interview Tips
- Compare: cookies (4KB, sent with requests), localStorage (5MB, persistent), sessionStorage (5MB, tab-scoped)
- Storage event for cross-tab sync is a real-time communication pattern
- XSS can read all Web Storage — prefer httpOnly cookies for tokens
- Web Storage is synchronous and can block the main thread for large data