ESC

Type to search the knowledge base.

localStorage and sessionStorage

Web Storage APIs — persistence, quota, JSON serialization, privacy modes, and when cookies or IndexedDB fit better.

beginner3 min read
  • javascript
  • localstorage
  • sessionstorage
  • web-storage

localStorage and sessionStorage are synchronous key/value string maps scoped to an origin. They’re perfect for UI prefs and light caches. They’re wrong for secrets, large binary data, and anything that needs querying.

Differences

localStorage sessionStorage
Lifetime until cleared until tab/window closes
Shared tabs same origin shares per tab (mostly)
Survives restart yes no
localStorage.setItem('theme', 'dark');
localStorage.getItem('theme'); // "dark"
localStorage.removeItem('theme');
localStorage.clear();

sessionStorage.setItem('wizardStep', '2');

Only strings. Objects need JSON:

function setJson(key, value) {
  localStorage.setItem(key, JSON.stringify(value));
}

function getJson(key, fallback = null) {
  const raw = localStorage.getItem(key);
  if (raw == null) return fallback;
  try {
    return JSON.parse(raw);
  } catch {
    return fallback;
  }
}

setJson('prefs', { density: 'compact' });

Storage events (cross-tab)

window.addEventListener('storage', (e) => {
  // fires in *other* documents on the origin, not the writer
  if (e.key === 'theme') applyTheme(e.newValue);
});

Useful for syncing logout or theme across tabs.

Quota and errors

try {
  localStorage.setItem('cache', bigString);
} catch (err) {
  // QuotaExceededError in many browsers
  console.warn('storage full', err);
}

Rough per-origin limits are a few MB — not for video. Private browsing may throw or ephemeral-ize storage.

SSR / availability

function storageAvailable(type) {
  try {
    const s = window[type];
    const k = '__test__';
    s.setItem(k, '1');
    s.removeItem(k);
    return true;
  } catch {
    return false;
  }
}

Guard in SSR frameworks — window is missing on the server.

Security

  • Readable by any JS on the page → XSS steals tokens. Don’t store session tokens here; prefer HttpOnly cookies.
  • Not encrypted at rest.
  • Same-origin only — not a cross-site sharing tool.

When to choose something else

Need Use
Auth session HttpOnly cookie
Large structured offline data IndexedDB
Cache HTTP responses Cache API
Tiny sync prefs localStorage
Tab-scoped draft sessionStorage

Performance

Web Storage is synchronous and can block the main thread on large reads/writes. Keep payloads small; debounce writes on rapid updates.

// bad: write on every keystroke without debounce
// good:
const save = debounce((draft) => {
  sessionStorage.setItem('draft', draft);
}, 300);

Interview answer

“localStorage persists string key/values per origin; sessionStorage lasts for the tab session. I JSON-encode objects, try/catch quota errors, and listen to storage events for cross-tab sync. I never put auth tokens in web storage because XSS can read them. For large or queryable data I use IndexedDB.”

Versioning stored data

const KEY = 'prefs';
const VERSION = 2;

function loadPrefs() {
  const raw = localStorage.getItem(KEY);
  if (!raw) return { version: VERSION, theme: 'system' };
  try {
    const data = JSON.parse(raw);
    if (data.version !== VERSION) return migrate(data, VERSION);
    return data;
  } catch {
    localStorage.removeItem(KEY);
    return { version: VERSION, theme: 'system' };
  }
}

function savePrefs(prefs) {
  localStorage.setItem(KEY, JSON.stringify({ ...prefs, version: VERSION }));
}

Schemas evolve — store a version and migrate or drop. Corrupted JSON from partial writes is rare but real; never let a parse throw kill app boot.

Further reading

Related guides