Docs

Module-18-Browser-APIs-and-Storage

11.1 localStorage and sessionStorage

πŸ“‹ Table of Contents

  1. β€’Web Storage Overview
  2. β€’localStorage vs sessionStorage
  3. β€’Basic Operations
  4. β€’Storing Complex Data
  5. β€’Storage Events
  6. β€’Storage Limits
  7. β€’Best Practices

Web Storage Overview

Web Storage provides mechanisms for storing key-value pairs in the browser.

Storage Types

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Browser Storage Options                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                     β”‚                                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚   β”‚ localStorageβ”‚   β”‚   β”‚sessionStorageβ”‚   β”‚   Cookies   β”‚      β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜   β”‚   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚          β”‚          β”‚          β”‚                  β”‚              β”‚
β”‚    Persistent       β”‚    Tab-only            Sent to server     β”‚
β”‚    ~5-10 MB         β”‚    ~5-10 MB            ~4 KB limit        β”‚
β”‚    No expiry        β”‚    Until tab close     Has expiry         β”‚
β”‚                     β”‚                                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

When to Use Each

Use CaseStorage Type
User preferenceslocalStorage
Shopping cart (persistent)localStorage
Form draft (temporary)sessionStorage
One-time messagessessionStorage
Multi-tab sync neededlocalStorage
Sensitive session datasessionStorage
Auth tokens (simple apps)localStorage/sessionStorage
Tracking (cross-site)Cookies

localStorage vs sessionStorage

Comparison Table

FeaturelocalStoragesessionStorage
PersistenceUntil clearedUntil tab closes
ScopeOrigin-widePer tab/window
Capacity~5-10 MB~5-10 MB
Shared across tabsβœ… Yes❌ No
Survives refreshβœ… Yesβœ… Yes
Survives browser restartβœ… Yes❌ No
Sent to server❌ No❌ No

Visual Comparison

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         BROWSER                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚                    localStorage                          β”‚    β”‚
β”‚  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚    β”‚
β”‚  β”‚   β”‚  Shared across ALL tabs for same origin        β”‚    β”‚    β”‚
β”‚  β”‚   β”‚  example.com/page1 ←→ example.com/page2        β”‚    β”‚    β”‚
β”‚  β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”‚
β”‚  β”‚ Tab 1            β”‚  β”‚ Tab 2            β”‚                     β”‚
β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚                     β”‚
β”‚  β”‚ β”‚sessionStorageβ”‚ β”‚  β”‚ β”‚sessionStorageβ”‚ β”‚  ← Separate!       β”‚
β”‚  β”‚ β”‚  (isolated)  β”‚ β”‚  β”‚ β”‚  (isolated)  β”‚ β”‚                     β”‚
β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚                     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Lifecycle

localStorage Lifecycle:
─────────────────────────────────────────────────────────────────►
  Page Load        Page Refresh      Browser Close      Days Later
      β”‚                 β”‚                  β”‚                 β”‚
      β–Ό                 β–Ό                  β–Ό                 β–Ό
   β”Œβ”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”
   β”‚Data β”‚           β”‚Data β”‚           β”‚Data β”‚           β”‚Data β”‚
   β”‚EXISTSβ”‚          β”‚EXISTSβ”‚          β”‚EXISTSβ”‚          β”‚EXISTSβ”‚
   β””β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”˜


sessionStorage Lifecycle:
─────────────────────────────────────────────────────────────────►
  Page Load        Page Refresh      Tab/Window Close
      β”‚                 β”‚                  β”‚
      β–Ό                 β–Ό                  β–Ό
   β”Œβ”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”
   β”‚Data β”‚           β”‚Data β”‚           β”‚Data β”‚
   β”‚EXISTSβ”‚          β”‚EXISTSβ”‚          β”‚ GONE β”‚
   β””β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”˜

Basic Operations

Both localStorage and sessionStorage share the same API:

Core Methods

// Store a value
localStorage.setItem('key', 'value');
sessionStorage.setItem('key', 'value');

// Retrieve a value
const value = localStorage.getItem('key');
const value2 = sessionStorage.getItem('key');

// Remove a specific item
localStorage.removeItem('key');
sessionStorage.removeItem('key');

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

// Get number of items
const count = localStorage.length;

// Get key by index
const key = localStorage.key(0);

Methods Reference

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Storage Methods                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Method                β”‚ Description                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ setItem(key, value)   β”‚ Store value (converts to string)        β”‚
β”‚ getItem(key)          β”‚ Retrieve value (returns null if none)   β”‚
β”‚ removeItem(key)       β”‚ Delete specific key                     β”‚
β”‚ clear()               β”‚ Remove ALL items                        β”‚
β”‚ key(index)            β”‚ Get key name by index                   β”‚
β”‚ length                β”‚ Number of stored items (property)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Alternative Syntax

// Dot notation (works but not recommended)
localStorage.username = 'john';
const user = localStorage.username;

// Bracket notation
localStorage['username'] = 'john';
const user = localStorage['username'];

// Why use setItem/getItem instead?
// 1. Clearer intent
// 2. getItem returns null (not undefined) for missing keys
// 3. Avoids conflicts with built-in properties
// 4. Works with keys that aren't valid identifiers

Checking for Existence

// Check if key exists
if (localStorage.getItem('theme') !== null) {
  console.log('Theme is set');
}

// Using 'in' operator (works but less reliable)
if ('theme' in localStorage) {
  console.log('Theme exists');
}

// Check storage availability
function isStorageAvailable(type) {
  try {
    const storage = window[type];
    const test = '__storage_test__';
    storage.setItem(test, test);
    storage.removeItem(test);
    return true;
  } catch (e) {
    return false;
  }
}

if (isStorageAvailable('localStorage')) {
  // Safe to use localStorage
}

Storing Complex Data

Storage only accepts strings! Non-strings are converted automatically.

The Problem

// Numbers become strings
localStorage.setItem('count', 42);
console.log(localStorage.getItem('count')); // "42" (string!)
console.log(typeof localStorage.getItem('count')); // "string"

// Booleans become strings
localStorage.setItem('active', true);
console.log(localStorage.getItem('active')); // "true" (string!)
console.log(localStorage.getItem('active') === true); // false!

// Objects become "[object Object]"
localStorage.setItem('user', { name: 'John' });
console.log(localStorage.getItem('user')); // "[object Object]" 😱

The Solution: JSON

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Storing Complex Data                          β”‚
β”‚                                                                  β”‚
β”‚    JavaScript Object                    localStorage             β”‚
β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚    β”‚ {               β”‚   stringify()   β”‚                 β”‚      β”‚
β”‚    β”‚   name: "John", β”‚ ───────────────►│ '{"name":"John",β”‚      β”‚
β”‚    β”‚   age: 30       β”‚                 β”‚   "age":30}'    β”‚      β”‚
β”‚    β”‚ }               β”‚                 β”‚                 β”‚      β”‚
β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚                                                 β”‚                β”‚
β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚                β”‚
β”‚    β”‚ {               β”‚   parse()                β”‚                β”‚
β”‚    β”‚   name: "John", β”‚ β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚
β”‚    β”‚   age: 30       β”‚                                           β”‚
β”‚    β”‚ }               β”‚                                           β”‚
β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Storing Objects and Arrays

// Storing an object
const user = {
  name: 'John',
  email: 'john@example.com',
  preferences: {
    theme: 'dark',
    language: 'en',
  },
};

localStorage.setItem('user', JSON.stringify(user));

// Retrieving an object
const savedUser = JSON.parse(localStorage.getItem('user'));
console.log(savedUser.name); // "John"
console.log(savedUser.preferences.theme); // "dark"

// Storing an array
const todos = [
  { id: 1, text: 'Learn JavaScript', done: true },
  { id: 2, text: 'Learn Web Storage', done: false },
];

localStorage.setItem('todos', JSON.stringify(todos));

// Retrieving an array
const savedTodos = JSON.parse(localStorage.getItem('todos'));
savedTodos.forEach((todo) => console.log(todo.text));

Helper Functions

// Generic storage helpers
const storage = {
  set(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  },

  get(key, defaultValue = null) {
    const item = localStorage.getItem(key);
    if (item === null) return defaultValue;

    try {
      return JSON.parse(item);
    } catch (e) {
      return item; // Return as string if not valid JSON
    }
  },

  remove(key) {
    localStorage.removeItem(key);
  },

  clear() {
    localStorage.clear();
  },
};

// Usage
storage.set('user', { name: 'John', age: 30 });
const user = storage.get('user');
const missing = storage.get('nonexistent', { default: true });

Handling Dates

// Dates need special handling (JSON converts to string)
const event = {
  name: 'Meeting',
  date: new Date('2024-12-15'),
};

localStorage.setItem('event', JSON.stringify(event));
const saved = JSON.parse(localStorage.getItem('event'));

console.log(saved.date); // "2024-12-15T00:00:00.000Z" (string!)
console.log(new Date(saved.date)); // Date object again

// Use a reviver function for automatic conversion
function dateReviver(key, value) {
  // ISO 8601 date pattern
  const datePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
  if (typeof value === 'string' && datePattern.test(value)) {
    return new Date(value);
  }
  return value;
}

const restored = JSON.parse(localStorage.getItem('event'), dateReviver);
console.log(restored.date instanceof Date); // true

Storage Events

The storage event fires when storage changes in another tab/window.

Event Properties

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    StorageEvent Properties                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Property       β”‚ Description                                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ key            β”‚ The key that changed (null if clear())        β”‚
β”‚ oldValue       β”‚ Previous value (null if new key)              β”‚
β”‚ newValue       β”‚ New value (null if removed)                   β”‚
β”‚ url            β”‚ URL of page that made the change              β”‚
β”‚ storageArea    β”‚ The storage object (localStorage/session)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Listening for Changes

// Listen for storage changes from OTHER tabs
window.addEventListener('storage', function (e) {
  console.log('Storage changed!');
  console.log('Key:', e.key);
  console.log('Old value:', e.oldValue);
  console.log('New value:', e.newValue);
  console.log('URL:', e.url);

  // React to specific changes
  if (e.key === 'theme') {
    applyTheme(e.newValue);
  }

  if (e.key === 'user' && e.newValue === null) {
    // User logged out in another tab
    redirectToLogin();
  }
});

Cross-Tab Communication

Tab 1                                    Tab 2
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                            β”‚          β”‚                            β”‚
β”‚ localStorage.setItem(      β”‚          β”‚ window.addEventListener(   β”‚
β”‚   'message',               β”‚   ────►  β”‚   'storage', handler       β”‚
β”‚   'Hello from Tab 1'       β”‚          β”‚ );                         β”‚
β”‚ );                         β”‚          β”‚                            β”‚
β”‚                            β”‚          β”‚ // handler receives event  β”‚
β”‚                            β”‚          β”‚ // with key='message'      β”‚
β”‚                            β”‚          β”‚ // newValue='Hello...'     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Note: Tab 1 does NOT receive the storage event!
      Only OTHER tabs/windows do.

Same-Tab Updates

// For same-tab updates, create your own event system
class StorageManager {
  constructor() {
    this.listeners = new Map();
  }

  set(key, value) {
    const oldValue = localStorage.getItem(key);
    const newValue = JSON.stringify(value);
    localStorage.setItem(key, newValue);

    // Notify local listeners
    this._notify(key, oldValue, newValue);
  }

  get(key) {
    const item = localStorage.getItem(key);
    return item ? JSON.parse(item) : null;
  }

  onChange(key, callback) {
    if (!this.listeners.has(key)) {
      this.listeners.set(key, []);
    }
    this.listeners.get(key).push(callback);
  }

  _notify(key, oldValue, newValue) {
    const callbacks = this.listeners.get(key) || [];
    callbacks.forEach((cb) =>
      cb({
        key,
        oldValue,
        newValue,
      })
    );
  }
}

const store = new StorageManager();
store.onChange('count', (e) => console.log('Count changed:', e.newValue));
store.set('count', 42); // Logs: Count changed: 42

Storage Limits

Size Limits

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Storage Limits by Browser                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Browser            β”‚ localStorage + sessionStorage Limit        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Chrome             β”‚ ~5 MB per origin                           β”‚
β”‚ Firefox            β”‚ ~5 MB per origin                           β”‚
β”‚ Safari             β”‚ ~5 MB per origin                           β”‚
β”‚ Edge               β”‚ ~5 MB per origin                           β”‚
β”‚ Mobile Safari      β”‚ ~5 MB per origin                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Note: "per origin" means protocol + domain + port
      https://example.com and http://example.com are different!

Checking Available Space

// Estimate storage usage (modern browsers)
if (navigator.storage && navigator.storage.estimate) {
  navigator.storage.estimate().then((estimate) => {
    console.log('Usage:', estimate.usage);
    console.log('Quota:', estimate.quota);
    console.log(
      'Percent used:',
      ((estimate.usage / estimate.quota) * 100).toFixed(2) + '%'
    );
  });
}

// Calculate current localStorage usage
function getLocalStorageSize() {
  let total = 0;
  for (let key in localStorage) {
    if (localStorage.hasOwnProperty(key)) {
      // Each character is 2 bytes in JavaScript (UTF-16)
      total += (key.length + localStorage[key].length) * 2;
    }
  }
  return total;
}

console.log('Current usage:', getLocalStorageSize(), 'bytes');
console.log('Current usage:', (getLocalStorageSize() / 1024).toFixed(2), 'KB');

Handling Quota Exceeded

function safeSetItem(key, value) {
  try {
    localStorage.setItem(key, JSON.stringify(value));
    return true;
  } catch (e) {
    if (
      e.name === 'QuotaExceededError' ||
      e.name === 'NS_ERROR_DOM_QUOTA_REACHED'
    ) {
      console.error('Storage quota exceeded');

      // Possible recovery strategies:
      // 1. Clear old data
      // 2. Compress data
      // 3. Use IndexedDB instead
      // 4. Alert user

      return false;
    }
    throw e; // Re-throw other errors
  }
}

// Usage
if (!safeSetItem('largeData', hugeObject)) {
  // Handle storage full scenario
  cleanupOldData();
}

Best Practices

1. Use Namespaced Keys

// Bad - risk of conflicts
localStorage.setItem('user', data);
localStorage.setItem('theme', 'dark');

// Good - namespace your keys
const APP_PREFIX = 'myApp_';
localStorage.setItem(`${APP_PREFIX}user`, data);
localStorage.setItem(`${APP_PREFIX}theme`, 'dark');

// Or use an object structure
const appData = {
  user: { name: 'John' },
  theme: 'dark',
  settings: {
    /* ... */
  },
};
localStorage.setItem('myApp', JSON.stringify(appData));

2. Add Version Control

const STORAGE_VERSION = '1.2';

function saveData(data) {
  const wrapper = {
    version: STORAGE_VERSION,
    timestamp: Date.now(),
    data: data,
  };
  localStorage.setItem('myApp', JSON.stringify(wrapper));
}

function loadData() {
  const saved = localStorage.getItem('myApp');
  if (!saved) return null;

  const wrapper = JSON.parse(saved);

  if (wrapper.version !== STORAGE_VERSION) {
    // Handle migration or clear old data
    console.log('Storage version mismatch, migrating...');
    return migrateData(wrapper);
  }

  return wrapper.data;
}

3. Handle Errors Gracefully

class SafeStorage {
  static isAvailable() {
    try {
      const test = '__test__';
      localStorage.setItem(test, test);
      localStorage.removeItem(test);
      return true;
    } catch (e) {
      return false;
    }
  }

  static set(key, value) {
    if (!this.isAvailable()) {
      console.warn('Storage not available');
      return false;
    }

    try {
      localStorage.setItem(key, JSON.stringify(value));
      return true;
    } catch (e) {
      console.error('Storage error:', e);
      return false;
    }
  }

  static get(key, defaultValue = null) {
    if (!this.isAvailable()) {
      return defaultValue;
    }

    try {
      const item = localStorage.getItem(key);
      return item ? JSON.parse(item) : defaultValue;
    } catch (e) {
      console.error('Storage read error:', e);
      return defaultValue;
    }
  }
}

4. Don't Store Sensitive Data

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    ⚠️ Security Warning                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                  β”‚
β”‚  NEVER store in localStorage/sessionStorage:                    β”‚
β”‚                                                                  β”‚
β”‚  βœ— Passwords (plain or hashed)                                  β”‚
β”‚  βœ— Credit card numbers                                          β”‚
β”‚  βœ— Social security numbers                                      β”‚
β”‚  βœ— API keys with write access                                   β”‚
β”‚  βœ— Private encryption keys                                      β”‚
β”‚                                                                  β”‚
β”‚  Why?                                                            β”‚
β”‚  - XSS attacks can read all storage                             β”‚
β”‚  - Browser extensions can access storage                        β”‚
β”‚  - No encryption by default                                     β”‚
β”‚  - Persists even after logout (localStorage)                    β”‚
β”‚                                                                  β”‚
β”‚  For auth tokens: Consider httpOnly cookies instead             β”‚
β”‚                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

5. Clean Up Old Data

// Add expiration to stored data
function setWithExpiry(key, value, ttlMs) {
  const item = {
    value: value,
    expiry: Date.now() + ttlMs,
  };
  localStorage.setItem(key, JSON.stringify(item));
}

function getWithExpiry(key) {
  const itemStr = localStorage.getItem(key);
  if (!itemStr) return null;

  const item = JSON.parse(itemStr);

  if (Date.now() > item.expiry) {
    localStorage.removeItem(key);
    return null;
  }

  return item.value;
}

// Set item that expires in 1 hour
setWithExpiry('tempData', { foo: 'bar' }, 60 * 60 * 1000);

Quick Reference

Cheat Sheet

// Basic operations
localStorage.setItem('key', 'value'); // Store
localStorage.getItem('key'); // Retrieve (null if missing)
localStorage.removeItem('key'); // Delete
localStorage.clear(); // Clear all
localStorage.length; // Count
localStorage.key(0); // Get key by index

// Store objects/arrays
localStorage.setItem('obj', JSON.stringify({ a: 1 }));
const obj = JSON.parse(localStorage.getItem('obj'));

// Session storage (same API)
sessionStorage.setItem('key', 'value');

// Storage event (cross-tab only)
window.addEventListener('storage', (e) => {
  console.log(e.key, e.oldValue, e.newValue);
});

Summary

ConceptKey Points
localStoragePersists forever, shared across tabs
sessionStorageCleared when tab closes, isolated per tab
API MethodssetItem, getItem, removeItem, clear, key, length
Complex DataUse JSON.stringify/parse for objects/arrays
Storage EventFires in OTHER tabs when storage changes
Limits~5 MB per origin
SecurityDon't store sensitive data, accessible to XSS
Best PracticesNamespace keys, version data, handle errors
Module 18 Browser APIs And Storage - JavaScript Tutorial | DeepML