Ad – 728Γ—90
🌐 DOM & Browser

JavaScript Web Storage – Storing Data in the Browser

The Web Storage API lets JavaScript persist data directly in the browser β€” no server required. localStorage survives browser restarts; sessionStorage lives only as long as the tab is open. Together they provide a simple key-value store for user preferences, cached data, and application state.

⏱️ 18 min read 🎯 Intermediate πŸ“… Updated 2026

localStorage – Persistent Storage

localStorage stores data with no expiration time. Data persists across browser sessions, tabs (same origin), and restarts. The limit is approximately 5 MB per origin (varies by browser).

JavaScript
// setItem β€” store a value (strings only!)
localStorage.setItem('username', 'alice');
localStorage.setItem('theme', 'dark');

// getItem β€” retrieve (returns null if key doesn't exist)
const username = localStorage.getItem('username');
console.log(username); // "alice"

const missing = localStorage.getItem('nonexistent');
console.log(missing); // null

// removeItem β€” delete a single key
localStorage.removeItem('theme');

// clear β€” delete ALL keys in this origin (use with caution!)
// localStorage.clear();

// length β€” number of stored items
console.log(localStorage.length); // 1

// key(index) β€” get key name by index (order not guaranteed)
console.log(localStorage.key(0)); // "username"

// Direct property access (works but not recommended β€” conflicts with built-in props)
localStorage.color = 'blue';  // ❌ avoid
localStorage.setItem('color', 'blue'); // βœ… preferred
β–Ά Output
username β†’ "alice"
missing  β†’ null
length   β†’ 1
key(0)   β†’ "username"

sessionStorage – Tab-Scoped Storage

sessionStorage has the same API as localStorage but data is cleared when the tab (or window) is closed. Opening the same URL in a new tab creates a completely separate storage scope.

JavaScript
// Identical API to localStorage
sessionStorage.setItem('currentStep', '2');
sessionStorage.setItem('formDraft', JSON.stringify({ name: 'Bob', email: 'bob@mail.com' }));

const step = sessionStorage.getItem('currentStep');
console.log(step); // "2"

// Use cases:
// - Wizard/multi-step form progress (lose if user closes tab β€” by design)
// - Temporary shopping cart (before checkout)
// - One-time messages / alerts per session
// - Protecting against CSRF tokens (very short-lived)

sessionStorage.removeItem('currentStep');
// Cleared automatically when tab closes β€” no manual cleanup needed

Storing Objects with JSON

Web Storage only stores strings. Use JSON.stringify() to store objects and JSON.parse() to restore them:

JavaScript
// Store an object
const user = { name: 'Alice', role: 'admin', preferences: { theme: 'dark', lang: 'en' } };
localStorage.setItem('user', JSON.stringify(user));

// Retrieve and parse
const stored = localStorage.getItem('user');
const parsedUser = stored ? JSON.parse(stored) : null;
console.log(parsedUser.preferences.theme); // "dark"

// Store an array
const history = ['home', 'about', 'products'];
localStorage.setItem('navHistory', JSON.stringify(history));
const navHistory = JSON.parse(localStorage.getItem('navHistory') || '[]');
navHistory.push('contact');
localStorage.setItem('navHistory', JSON.stringify(navHistory));

// Helper wrapper to avoid repetition
const store = {
  set(key, val) { localStorage.setItem(key, JSON.stringify(val)); },
  get(key, fallback = null) {
    const item = localStorage.getItem(key);
    return item !== null ? JSON.parse(item) : fallback;
  },
  remove(key) { localStorage.removeItem(key); }
};

store.set('settings', { fontSize: 16, color: '#333' });
const settings = store.get('settings', { fontSize: 14, color: '#000' });

The storage Event – Cross-Tab Sync

When localStorage is changed in one tab, a storage event fires in all other tabs of the same origin β€” enabling real-time cross-tab communication:

JavaScript
// Listen for changes made in OTHER tabs
window.addEventListener('storage', (event) => {
  console.log('Key changed:', event.key);
  console.log('Old value:', event.oldValue);
  console.log('New value:', event.newValue);
  console.log('Storage area:', event.storageArea); // localStorage or sessionStorage
  console.log('Origin URL:', event.url);

  // Example: sync theme across tabs
  if (event.key === 'theme') {
    document.body.dataset.theme = event.newValue;
  }

  // Cross-tab logout
  if (event.key === 'auth_token' && event.newValue === null) {
    redirectToLogin();
  }
});

// Note: the storage event does NOT fire in the tab that made the change
// It ONLY fires in other tabs/windows
πŸ’‘
Practical: Persist User Theme Preference

Store the theme choice in localStorage and read it on page load before rendering to avoid flash of wrong theme (FOUC). Apply the theme class to <html> or <body> at the very top of <head> β€” before any CSS is applied.

IndexedDB – Large Structured Data

IndexedDB is a browser-native NoSQL database for large volumes of structured data. It supports transactions, indexes, cursors, and blob storage β€” but has a complex API. Libraries like Dexie.js make it practical:

JavaScript
// Raw IndexedDB API (simplified example)
const request = indexedDB.open('myDatabase', 1);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  // Create an object store (like a table)
  const store = db.createObjectStore('users', { keyPath: 'id' });
  store.createIndex('email', 'email', { unique: true });
};

request.onsuccess = (event) => {
  const db = event.target.result;

  // Write a record
  const tx = db.transaction('users', 'readwrite');
  tx.objectStore('users').put({ id: 1, name: 'Alice', email: 'alice@example.com' });
  tx.oncomplete = () => console.log('User saved');
};

// Using Dexie.js (much cleaner):
// const db = new Dexie('myDatabase');
// db.version(1).stores({ users: '++id, name, email' });
// await db.users.add({ name: 'Alice', email: 'alice@example.com' });
// const alice = await db.users.where('email').equals('alice@example.com').first();

Storage Options Comparison

FeatureCookieslocalStoragesessionStorageIndexedDB
Capacity~4 KB~5 MB~5 MBHundreds of MB+
LifetimeSet by expiry / sessionPermanentUntil tab closesPermanent
Sent with requestsYes (automatic)NoNoNo
Accessible from JSYes (unless HttpOnly)YesYesYes (async)
Cross-tab sharingYesYes (same origin)NoYes (same origin)
Data formatStringStringStringAny structured data
Use caseAuth, trackingPreferences, cacheForm draft, wizardOffline apps, large data
🚫
Never store sensitive data in Web Storage

localStorage and sessionStorage are accessible by any JavaScript on the page, including injected third-party scripts. Never store passwords, authentication tokens (JWTs), credit card numbers, or any personally identifiable information. For auth tokens, prefer HttpOnly cookies which are invisible to JavaScript.

Practical: Persisting Theme and Preferences

JavaScript
// Theme persistence
const themeBtn = document.getElementById('themeToggle');

// Apply saved theme on load
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);

themeBtn.addEventListener('click', () => {
  const current = document.documentElement.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);
});

// Persist font size preference
const fontSizeControl = document.getElementById('fontSize');
const savedSize = localStorage.getItem('fontSize') || '16';
document.body.style.fontSize = savedSize + 'px';
fontSizeControl.value = savedSize;

fontSizeControl.addEventListener('input', () => {
  const size = fontSizeControl.value;
  document.body.style.fontSize = size + 'px';
  localStorage.setItem('fontSize', size);
});

πŸ‹οΈ Practical Exercise

Build a persistent notes app:

  1. A <textarea> for note input and a "Save" button.
  2. On save, add the note to an array stored in localStorage under the key 'notes'.
  3. On page load, read the array from localStorage and render all notes as cards.
  4. Each card has a "Delete" button that removes the note from the array and updates localStorage.
  5. Show the count of saved notes.

πŸ”₯ Challenge Exercise

Build a multi-tab counter using the storage event. Page A has increment/decrement buttons. Page B displays the current count. Any change on page A should instantly update page B via the storage event (without polling). Both pages should also persist the count across page refreshes. Test by opening both pages in separate tabs.

Summary

πŸ“‹ Summary

  • localStorage: persistent, ~5 MB, same origin, string-only values.
  • sessionStorage: same API, cleared when tab closes, not shared between tabs.
  • Use JSON.stringify/parse to store and retrieve objects and arrays.
  • The storage event fires in other tabs when localStorage changes β€” useful for cross-tab sync.
  • IndexedDB handles large structured data; use Dexie.js to simplify its API.
  • Never store passwords or auth tokens in Web Storage β€” use HttpOnly cookies instead.
  • Always provide a fallback value when reading from storage in case the key doesn't exist.

Interview Questions

  • What is the difference between localStorage and sessionStorage?
  • Why can't you store objects directly in localStorage?
  • How would you implement cross-tab communication using Web Storage?
  • Why should you avoid storing JWT tokens in localStorage?
  • When would you choose IndexedDB over localStorage?

Frequently Asked Questions

What happens when localStorage is full (quota exceeded)? +

The browser throws a DOMException with the name QuotaExceededError. Wrap write operations in a try/catch to handle this gracefully: catch the error, inform the user, or remove old data to make room with localStorage.removeItem() before retrying.

Can different websites access my localStorage data? +

No. Web Storage is scoped to the origin (scheme + domain + port). https://example.com cannot read https://other.com's storage, and http://example.com cannot read https://example.com's storage. However, all scripts running on the same origin can access the same storage β€” including ads and analytics scripts loaded on your page.

Does Private/Incognito mode affect Web Storage? +

Yes. In private/incognito mode, localStorage is isolated to the private session and is cleared when the window closes β€” behaving like sessionStorage. On some browsers (older Safari), localStorage.setItem() may throw in private mode due to quota being set to zero. Always wrap writes in try/catch.

Ad – 336Γ—280