Web Storage

Easy

Web 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

Related Concepts