LocalStorage, SessionStorage, and IndexedDB

Alex Chang Feb 2026
1 tab
// 1. localStorage basics
// Store data
localStorage.setItem('username', 'alex');
localStorage.setItem('theme', 'dark');

// Retrieve data
const username = localStorage.getItem('username'); // 'alex'

// Remove data
localStorage.removeItem('username');

// Clear all
localStorage.clear();

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

// 2. Storing objects (JSON serialization)
const user = {
  id: 1,
  name: 'Alex',
  settings: { theme: 'dark', language: 'en' }
};

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

// Retrieve
const storedUser = JSON.parse(localStorage.getItem('user'));

// 3. Helper functions
const storage = {
  set(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value));
      return true;
    } catch (error) {
      console.error('Storage error:', error);
      return false;
    }
  },

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

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

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

  keys() {
    return Object.keys(localStorage);
  },

  has(key) {
    return localStorage.getItem(key) !== null;
  }
};

// Usage
storage.set('user', { name: 'Alex' });
const user = storage.get('user', {});

// 4. Storage with expiration
const storageWithExpiry = {
  set(key, value, ttl) {
    const now = new Date();
    const item = {
      value: value,
      expiry: ttl ? now.getTime() + ttl : null,
    };
    localStorage.setItem(key, JSON.stringify(item));
  },

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

    const item = JSON.parse(itemStr);
    const now = new Date();

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

    return item.value;
  }
};

// Usage
storageWithExpiry.set('token', 'abc123', 3600000); // 1 hour

// 5. sessionStorage (same API, different scope)
sessionStorage.setItem('tempData', 'value');
const tempData = sessionStorage.getItem('tempData');

// 6. Storage events (synchronize across tabs)
window.addEventListener('storage', (event) => {
  console.log('Storage changed!');
  console.log('Key:', event.key);
  console.log('Old value:', event.oldValue);
  console.log('New value:', event.newValue);
  console.log('URL:', event.url);

  // Update UI based on changes
  if (event.key === 'theme') {
    updateTheme(event.newValue);
  }
});

// 7. Check storage quota
if ('storage' in navigator && 'estimate' in navigator.storage) {
  navigator.storage.estimate().then(({ usage, quota }) => {
    console.log(`Using ${usage} bytes of ${quota} bytes.`);
    const percentUsed = (usage / quota * 100).toFixed(2);
    console.log(`Storage used: ${percentUsed}%`);
  });
}

// 8. IndexedDB - structured storage
let db;

// Open database
const request = indexedDB.open('MyDatabase', 1);

request.onerror = () => {
  console.error('Database failed to open');
};

request.onsuccess = () => {
  db = request.result;
  console.log('Database opened successfully');
};

// Create object stores
request.onupgradeneeded = (event) => {
  db = event.target.result;

  // Create object store
  const objectStore = db.createObjectStore('users', {
    keyPath: 'id',
    autoIncrement: true
  });

  // Create indexes
  objectStore.createIndex('email', 'email', { unique: true });
  objectStore.createIndex('name', 'name', { unique: false });

  console.log('Database setup complete');
};

// 9. IndexedDB CRUD operations
const dbOperations = {
  // Create
  async add(storeName, data) {
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([storeName], 'readwrite');
      const objectStore = transaction.objectStore(storeName);
      const request = objectStore.add(data);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  },

  // Read
  async get(storeName, key) {
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([storeName]);
      const objectStore = transaction.objectStore(storeName);
      const request = objectStore.get(key);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  },

  // Update
  async put(storeName, data) {
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([storeName], 'readwrite');
      const objectStore = transaction.objectStore(storeName);
      const request = objectStore.put(data);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  },

  // Delete
  async delete(storeName, key) {
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([storeName], 'readwrite');
      const objectStore = transaction.objectStore(storeName);
      const request = objectStore.delete(key);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  },

  // Get all
  async getAll(storeName) {
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([storeName]);
      const objectStore = transaction.objectStore(storeName);
      const request = objectStore.getAll();

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  },

  // Query by index
  async getByIndex(storeName, indexName, value) {
    return new Promise((resolve, reject) => {
      const transaction = db.transaction([storeName]);
      const objectStore = transaction.objectStore(storeName);
      const index = objectStore.index(indexName);
      const request = index.get(value);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
};

// Usage
async function example() {
  // Add user
  await dbOperations.add('users', {
    name: 'Alex',
    email: 'alex@example.com',
    age: 30
  });

  // Get user by ID
  const user = await dbOperations.get('users', 1);

  // Update user
  await dbOperations.put('users', {
    id: 1,
    name: 'Alex Chen',
    email: 'alex@example.com',
    age: 31
  });

  // Get user by email (using index)
  const userByEmail = await dbOperations.getByIndex(
    'users',
    'email',
    'alex@example.com'
  );

  // Get all users
  const allUsers = await dbOperations.getAll('users');

  // Delete user
  await dbOperations.delete('users', 1);
}
1 file · javascript Explain with highlit

Web storage APIs persist data in the browser. I use localStorage for permanent client-side storage across sessions. The sessionStorage API stores data for single sessions that clears when tabs close. Both provide simple key-value storage with 5-10MB limits. IndexedDB offers larger storage (50MB+) with transactional database features. Storage events synchronize data across tabs. JSON serialization stores complex objects. IndexedDB supports indexes, cursors, and queries for structured data. Storage quotas prevent abuse. Understanding storage options enables offline functionality and better UX.