Web Storage

Difficulty: Intermediate

The Web Storage API provides two mechanisms for storing key-value pairs in the browser: localStorage and sessionStorage. Both store data as strings, have a synchronous API, and offer approximately 5-10 MB of storage per origin (compared to the 4 KB limit of cookies). Understanding the differences between these storage mechanisms and cookies is a frequent interview topic.

localStorage persists data with no expiration. Data stored in localStorage remains available even after the browser is closed and reopened, across tabs and windows of the same origin, and survives browser restarts. It is ideal for user preferences, theme settings, cached data, and any information that should persist across sessions. Data is cleared only when the user explicitly clears browser data or your code calls localStorage.clear() or removeItem().

sessionStorage is scoped to a single browser tab or window. Data persists across page reloads within the same tab but is deleted when the tab is closed. Opening a new tab to the same URL creates a separate sessionStorage. This makes it suitable for single-session data like form progress, wizard state, or temporary authentication tokens. Each tab has its own isolated sessionStorage - data does not leak between tabs.

Cookies predate the Web Storage API and serve a different purpose. Cookies are automatically sent with every HTTP request to the matching domain, making them suitable for authentication tokens and server-side session management. They have a 4 KB size limit, support expiration dates (expires/max-age), and can be flagged as HttpOnly (inaccessible to JavaScript), Secure (HTTPS only), and SameSite (CSRF protection). Unlike localStorage and sessionStorage, cookies are part of the HTTP protocol, not just a client-side API.

Both localStorage and sessionStorage share the same API: setItem(key, value), getItem(key), removeItem(key), clear(), key(index), and the length property. Since values are always strings, you must use JSON.stringify() when storing objects or arrays and JSON.parse() when retrieving them. Failing to serialize complex data types is one of the most common bugs. The storage event fires on other tabs/windows when localStorage changes, enabling cross-tab communication.

Code examples

localStorage Basics

// Store simple values
localStorage.setItem('username', 'john_doe');
localStorage.setItem('theme', 'dark');
localStorage.setItem('fontSize', '16');

// Retrieve values
const username = localStorage.getItem('username');
console.log(username); // 'john_doe'

// Store complex data with JSON
const user = {
  name: 'John',
  email: 'john@example.com',
  preferences: { theme: 'dark', language: 'en' }
};
localStorage.setItem('user', JSON.stringify(user));

// Retrieve and parse
const stored = JSON.parse(localStorage.getItem('user'));
console.log(stored.name);                  // 'John'
console.log(stored.preferences.theme);      // 'dark'

// Remove specific item
localStorage.removeItem('fontSize');

// Clear all localStorage
// localStorage.clear();

// Check how many items are stored
console.log('Items stored:', localStorage.length);

setItem stores string key-value pairs. getItem retrieves them. For objects and arrays, serialize with JSON.stringify and deserialize with JSON.parse. removeItem deletes a single key; clear removes everything.

sessionStorage for Tab-Scoped Data

// Track form progress in current tab only
sessionStorage.setItem('formStep', '2');
sessionStorage.setItem('formData', JSON.stringify({
  name: 'Jane',
  email: 'jane@example.com'
}));

// Retrieve on page reload (same tab)
const step = sessionStorage.getItem('formStep');
const data = JSON.parse(sessionStorage.getItem('formData'));
console.log(`Step ${step}:`, data);

// Session counter (resets when tab closes)
let visits = parseInt(sessionStorage.getItem('tabVisits') || '0');
visits++;
sessionStorage.setItem('tabVisits', String(visits));
console.log(`Page views this session: ${visits}`);

// Opening a new tab to the same URL creates fresh sessionStorage
// Each tab is isolated from the others

sessionStorage works identically to localStorage in API, but data is scoped to the current tab. Closing the tab destroys the data. New tabs get fresh storage. Use it for data that should not persist beyond the current session.

Cookies vs localStorage vs sessionStorage

// COOKIES: sent with every HTTP request, 4KB limit
document.cookie = 'token=abc123; max-age=3600; path=/; Secure; SameSite=Strict';

// Reading cookies (string parsing required)
function getCookie(name) {
  const match = document.cookie.match(
    new RegExp('(^| )' + name + '=([^;]+)')
  );
  return match ? match[2] : null;
}
console.log('Token:', getCookie('token'));

// Delete a cookie (set max-age to 0)
document.cookie = 'token=; max-age=0; path=/';

// LOCAL STORAGE: 5-10MB, persists forever, not sent to server
localStorage.setItem('preferences', JSON.stringify({ theme: 'dark' }));

// SESSION STORAGE: 5-10MB, tab-scoped, not sent to server
sessionStorage.setItem('tempData', 'temporary');

// Summary:
// | Feature          | Cookies    | localStorage | sessionStorage |
// |-----------------|------------|--------------|----------------|
// | Size limit      | ~4 KB      | ~5-10 MB     | ~5-10 MB       |
// | Sent to server  | Yes        | No           | No             |
// | Expiration      | Settable   | Never        | Tab close      |
// | Scope           | Domain     | Origin       | Tab + Origin   |
// | API             | String     | Key-Value    | Key-Value      |

Cookies are for server communication (auth tokens). localStorage is for persistent client data (preferences). sessionStorage is for temporary tab data (form progress). Never store sensitive data in localStorage or sessionStorage - they are accessible to any script on the page.

Cross-Tab Communication with Storage Events

// Tab A: Listen for storage changes from other tabs
window.addEventListener('storage', (event) => {
  console.log('Storage changed in another tab!');
  console.log('Key:', event.key);
  console.log('Old value:', event.oldValue);
  console.log('New value:', event.newValue);
  console.log('URL:', event.url);

  // React to logout in another tab
  if (event.key === 'auth_token' && event.newValue === null) {
    console.log('User logged out in another tab. Redirecting...');
    window.location.href = '/login';
  }
});

// Tab B: Make a change that triggers the event in Tab A
localStorage.setItem('auth_token', 'new-token-value');

// Later, removing the token triggers the event in all other tabs
localStorage.removeItem('auth_token');

// Note: The storage event fires in OTHER tabs/windows
// of the same origin, NOT in the tab that made the change.

The storage event fires on all OTHER tabs/windows when localStorage changes. This enables cross-tab communication - useful for syncing logout, theme changes, or cart updates across multiple open tabs.

Key points

Concepts covered

localStorage, sessionStorage, Cookies, Storage Events, JSON Serialization