javascript-today

LocalStorage and SessionStorage

Web Storage provides two mechanisms for storing data in the browser: localStorage and sessionStorage. Both offer simple key-value storage without needing a database or server.

localStorage vs sessionStorage

Feature localStorage sessionStorage
Lifespan Persists forever Until tab closes
Scope Shared across all tabs One tab only
Capacity ~5-10 MB ~5-10 MB
Use case User preferences Temporary session data

localStorage Basics

// Store data
localStorage.setItem('username', 'Alice');
localStorage.setItem('theme', 'dark');

// Retrieve data
const username = localStorage.getItem('username');
console.log(username);  // "Alice"

// Remove item
localStorage.removeItem('theme');

// Clear all data
localStorage.clear();

// Check if key exists
if (localStorage.getItem('username') !== null) {
  console.log('Username is stored');
}

Storing Different Data Types

localStorage only stores strings. Convert other types:

// Numbers (convert to/from string)
localStorage.setItem('count', '42');
const count = parseInt(localStorage.getItem('count'));

// Booleans (convert to/from string)
localStorage.setItem('isLoggedIn', 'true');
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';

// Objects and Arrays (use JSON)
const user = {
  id: 123,
  name: 'Alice',
  email: 'alice@example.com'
};

localStorage.setItem('user', JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log(storedUser.name);  // "Alice"

// Arrays
const todos = ['Buy milk', 'Walk dog', 'Code'];
localStorage.setItem('todos', JSON.stringify(todos));
const storedTodos = JSON.parse(localStorage.getItem('todos'));

Helper Functions

Create utility functions for easier use:

const storage = {
  // Set any type of data
  set(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  },
  
  // Get any type of data
  get(key, defaultValue = null) {
    const value = localStorage.getItem(key);
    if (value === null) return defaultValue;
    
    try {
      return JSON.parse(value);
    } catch {
      return value;  // Return as string if not JSON
    }
  },
  
  // Remove item
  remove(key) {
    localStorage.removeItem(key);
  },
  
  // Clear all
  clear() {
    localStorage.clear();
  },
  
  // Check if key exists
  has(key) {
    return localStorage.getItem(key) !== null;
  }
};

// Usage
storage.set('user', { name: 'Alice', age: 30 });
storage.set('count', 42);
storage.set('active', true);

const user = storage.get('user');
const count = storage.get('count');
const theme = storage.get('theme', 'light');  // Default value

Real-World Examples

Dark Mode Toggle

// Check saved preference on page load
function loadTheme() {
  const theme = localStorage.getItem('theme') || 'light';
  document.body.className = theme;
  return theme;
}

// Toggle dark mode
function toggleDarkMode() {
  const currentTheme = document.body.className;
  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
  
  document.body.className = newTheme;
  localStorage.setItem('theme', newTheme);
}

// Apply on page load
loadTheme();

// Toggle button
document.querySelector('#theme-toggle').addEventListener('click', toggleDarkMode);

User Preferences

class UserPreferences {
  constructor() {
    this.key = 'userPreferences';
  }
  
  get() {
    const prefs = localStorage.getItem(this.key);
    return prefs ? JSON.parse(prefs) : this.defaults();
  }
  
  defaults() {
    return {
      theme: 'light',
      fontSize: 16,
      notifications: true,
      language: 'en'
    };
  }
  
  set(key, value) {
    const prefs = this.get();
    prefs[key] = value;
    localStorage.setItem(this.key, JSON.stringify(prefs));
  }
  
  apply() {
    const prefs = this.get();
    document.body.className = prefs.theme;
    document.body.style.fontSize = `${prefs.fontSize}px`;
    // Apply other preferences...
  }
}

// Usage
const preferences = new UserPreferences();
preferences.apply();  // Apply on page load

// Change preference
preferences.set('theme', 'dark');
preferences.set('fontSize', 18);
preferences.apply();

Shopping Cart

class ShoppingCart {
  constructor() {
    this.key = 'shoppingCart';
  }
  
  getItems() {
    const cart = localStorage.getItem(this.key);
    return cart ? JSON.parse(cart) : [];
  }
  
  addItem(product) {
    const items = this.getItems();
    
    // Check if item already in cart
    const existing = items.find(item => item.id === product.id);
    
    if (existing) {
      existing.quantity += 1;
    } else {
      items.push({ ...product, quantity: 1 });
    }
    
    localStorage.setItem(this.key, JSON.stringify(items));
    this.updateBadge();
  }
  
  removeItem(productId) {
    let items = this.getItems();
    items = items.filter(item => item.id !== productId);
    localStorage.setItem(this.key, JSON.stringify(items));
    this.updateBadge();
  }
  
  updateQuantity(productId, quantity) {
    const items = this.getItems();
    const item = items.find(i => i.id === productId);
    
    if (item) {
      item.quantity = quantity;
      localStorage.setItem(this.key, JSON.stringify(items));
      this.updateBadge();
    }
  }
  
  getTotalItems() {
    return this.getItems().reduce((sum, item) => sum + item.quantity, 0);
  }
  
  getTotalPrice() {
    return this.getItems().reduce((sum, item) => {
      return sum + (item.price * item.quantity);
    }, 0);
  }
  
  updateBadge() {
    const badge = document.querySelector('#cart-badge');
    if (badge) {
      badge.textContent = this.getTotalItems();
    }
  }
  
  clear() {
    localStorage.removeItem(this.key);
    this.updateBadge();
  }
}

// Usage
const cart = new ShoppingCart();

// Add to cart
cart.addItem({ id: 1, name: 'Laptop', price: 999 });
cart.addItem({ id: 2, name: 'Mouse', price: 29 });

// Display cart
const items = cart.getItems();
console.log('Total:', cart.getTotalPrice());

Form Auto-Save (Draft)

// Save form data as user types
function setupAutoSave(formId) {
  const form = document.querySelector(formId);
  const storageKey = `draft_${formId}`;
  
  // Load saved draft
  function loadDraft() {
    const draft = localStorage.getItem(storageKey);
    if (draft) {
      const data = JSON.parse(draft);
      Object.keys(data).forEach(key => {
        const field = form.querySelector(`[name="${key}"]`);
        if (field) field.value = data[key];
      });
    }
  }
  
  // Save draft
  function saveDraft() {
    const data = {};
    const inputs = form.querySelectorAll('input, textarea');
    inputs.forEach(input => {
      data[input.name] = input.value;
    });
    localStorage.setItem(storageKey, JSON.stringify(data));
  }
  
  // Clear draft
  function clearDraft() {
    localStorage.removeItem(storageKey);
  }
  
  // Auto-save on input
  form.addEventListener('input', saveDraft);
  
  // Clear draft on successful submit
  form.addEventListener('submit', (e) => {
    clearDraft();
  });
  
  // Load draft on page load
  loadDraft();
}

// Usage
setupAutoSave('#contact-form');

sessionStorage

Same API as localStorage, but data clears when tab closes:

// Store for this session only
sessionStorage.setItem('tempData', 'value');

// Retrieve
const temp = sessionStorage.getItem('tempData');

// Remove
sessionStorage.removeItem('tempData');

// Clear all
sessionStorage.clear();

Use cases for sessionStorage:

  • Multi-step forms (wizard state)
  • Temporary filters/search results
  • Authentication tokens (if you want logout on tab close)
  • Page navigation state

Multi-Step Form Example

class FormWizard {
  constructor() {
    this.key = 'wizardData';
  }
  
  saveStep(step, data) {
    const wizardData = this.getAllData();
    wizardData[step] = data;
    sessionStorage.setItem(this.key, JSON.stringify(wizardData));
  }
  
  getStep(step) {
    const wizardData = this.getAllData();
    return wizardData[step] || {};
  }
  
  getAllData() {
    const data = sessionStorage.getItem(this.key);
    return data ? JSON.parse(data) : {};
  }
  
  clear() {
    sessionStorage.removeItem(this.key);
  }
}

// Usage
const wizard = new FormWizard();

// Step 1: Personal info
wizard.saveStep('personal', {
  name: 'Alice',
  email: 'alice@example.com'
});

// Step 2: Address
wizard.saveStep('address', {
  street: '123 Main St',
  city: 'Boston'
});

// Final step: Get all data
const allData = wizard.getAllData();
console.log(allData);
// {
//   personal: { name: 'Alice', email: '...' },
//   address: { street: '...', city: '...' }
// }

Storage Events

Listen for storage changes (from other tabs):

// Listen for changes from other tabs
window.addEventListener('storage', (e) => {
  console.log('Key changed:', e.key);
  console.log('Old value:', e.oldValue);
  console.log('New value:', e.newValue);
  console.log('Storage area:', e.storageArea);
  
  // React to changes
  if (e.key === 'theme') {
    document.body.className = e.newValue;
  }
});

Note: Storage event only fires in other tabs, not the current tab.

Storage Limits and Errors

function safeSetItem(key, value) {
  try {
    localStorage.setItem(key, value);
    return true;
  } catch (e) {
    if (e.name === 'QuotaExceededError') {
      console.error('Storage quota exceeded!');
      // Handle: clear old data, notify user, etc.
    } else if (e.name === 'SecurityError') {
      console.error('Storage not available (private browsing?)');
    }
    return false;
  }
}

// Usage
if (!safeSetItem('bigData', hugeString)) {
  alert('Could not save data - storage full');
}

Checking Availability

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

// Check before using
if (storageAvailable('localStorage')) {
  localStorage.setItem('key', 'value');
} else {
  console.warn('localStorage not available');
  // Use cookies or in-memory storage as fallback
}

Best Practices

DO:

  • Always use try/catch for storage operations
  • Use JSON.stringify/parse for objects
  • Provide default values when getting items
  • Clear sensitive data when done
  • Check storage availability before using

DON’T:

  • Store sensitive data (passwords, credit cards)
  • Store large amounts of data (>5MB)
  • Assume storage is always available
  • Store executable code
  • Trust data from storage (validate it)

Security Considerations

// ❌ NEVER store sensitive data
localStorage.setItem('password', 'secret123');  // NO!
localStorage.setItem('creditCard', '1234...');  // NO!
localStorage.setItem('ssn', '123-45-6789');    // NO!

// ✅ Safe to store
localStorage.setItem('theme', 'dark');           // OK
localStorage.setItem('language', 'en');          // OK
localStorage.setItem('preferences', '...');      // OK

Why?

  • localStorage is accessible to all JavaScript on your domain
  • XSS attacks can steal data from localStorage
  • Data persists even after logout
  • Not encrypted

Summary

Operation localStorage sessionStorage
Set item localStorage.setItem(key, val) sessionStorage.setItem(key, val)
Get item localStorage.getItem(key) sessionStorage.getItem(key)
Remove item localStorage.removeItem(key) sessionStorage.removeItem(key)
Clear all localStorage.clear() sessionStorage.clear()
Lifespan Forever Until tab closes
Scope All tabs Single tab