Learning Objectives

  • Store and retrieve data with localStorage
  • Work with JSON data in storage
  • Understand storage limits and security
  • Handle storage events
  • Apply storage best practices

Basic Usage

// Store data (key-value pairs)
localStorage.setItem('username', 'John');
localStorage.setItem('theme', 'dark');
localStorage.setItem('fontSize', '16');

// Retrieve data
const username = localStorage.getItem('username'); // 'John'
const theme = localStorage.getItem('theme'); // 'dark'

// Alternative syntax
localStorage.username = 'Jane';
const name = localStorage.username;

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

// Clear all data
localStorage.clear();

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

// Get number of items
console.log(localStorage.length);

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

Storing Objects

Important: LocalStorage only stores strings. Use JSON.stringify() and JSON.parse() for objects.
// Store object
const user = {
    name: 'John Doe',
    email: 'john@example.com',
    age: 30,
    preferences: {
        theme: 'dark',
        notifications: true
    }
};

// Convert to JSON string
localStorage.setItem('user', JSON.stringify(user));

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

// Store array
const todos = ['Learn JavaScript', 'Build project', 'Deploy'];
localStorage.setItem('todos', JSON.stringify(todos));

const storedTodos = JSON.parse(localStorage.getItem('todos'));
console.log(storedTodos[0]); // 'Learn JavaScript'

Storage Helper Class

class Storage {
    static set(key, value) {
        try {
            const serialized = JSON.stringify(value);
            localStorage.setItem(key, serialized);
            return true;
        } catch (error) {
            console.error('Storage error:', error);
            return false;
        }
    }
    
    static 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;
        }
    }
    
    static remove(key) {
        localStorage.removeItem(key);
    }
    
    static clear() {
        localStorage.clear();
    }
    
    static has(key) {
        return localStorage.getItem(key) !== null;
    }
    
    static keys() {
        return Object.keys(localStorage);
    }
}

// Usage
Storage.set('user', { name: 'John', age: 30 });
const user = Storage.get('user');
const theme = Storage.get('theme', 'light'); // Default value

if (Storage.has('user')) {
    console.log('User data exists');
}

Storage.remove('user');

Storage with Expiration

function setWithExpiry(key, value, ttl) {
    const now = new Date();
    
    const item = {
        value: value,
        expiry: now.getTime() + ttl
    };
    
    localStorage.setItem(key, JSON.stringify(item));
}

function getWithExpiry(key) {
    const itemStr = localStorage.getItem(key);
    
    if (!itemStr) {
        return null;
    }
    
    const item = JSON.parse(itemStr);
    const now = new Date();
    
    // Check if expired
    if (now.getTime() > item.expiry) {
        localStorage.removeItem(key);
        return null;
    }
    
    return item.value;
}

// Store for 1 hour (3600000 ms)
setWithExpiry('authToken', 'abc123xyz', 3600000);

// Retrieve (returns null if expired)
const token = getWithExpiry('authToken');

if (token) {
    console.log('Token is valid:', token);
} else {
    console.log('Token expired or not found');
}

Storage Events

// Listen for storage changes from other tabs/windows
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);
    console.log('Storage area:', event.storageArea);
    
    // Sync UI with changes
    if (event.key === 'theme') {
        applyTheme(event.newValue);
    }
});

// Note: Storage event only fires in OTHER tabs/windows,
// not in the tab that made the change

Real-World Examples

Theme Persistence

class ThemeManager {
    static THEME_KEY = 'app-theme';
    
    static getTheme() {
        return localStorage.getItem(this.THEME_KEY) || 'light';
    }
    
    static setTheme(theme) {
        localStorage.setItem(this.THEME_KEY, theme);
        this.applyTheme(theme);
    }
    
    static applyTheme(theme) {
        document.body.className = `theme-${theme}`;
    }
    
    static init() {
        const theme = this.getTheme();
        this.applyTheme(theme);
    }
}

// On page load
ThemeManager.init();

// Toggle theme
function toggleTheme() {
    const current = ThemeManager.getTheme();
    const newTheme = current === 'light' ? 'dark' : 'light';
    ThemeManager.setTheme(newTheme);
}

Form Data Persistence

class FormPersistence {
    constructor(formId) {
        this.form = document.getElementById(formId);
        this.storageKey = `form-${formId}`;
        this.init();
    }
    
    init() {
        // Load saved data
        this.loadFormData();
        
        // Save on input
        this.form.addEventListener('input', () => {
            this.saveFormData();
        });
        
        // Clear on submit
        this.form.addEventListener('submit', () => {
            this.clearFormData();
        });
    }
    
    saveFormData() {
        const formData = new FormData(this.form);
        const data = Object.fromEntries(formData);
        localStorage.setItem(this.storageKey, JSON.stringify(data));
    }
    
    loadFormData() {
        const saved = localStorage.getItem(this.storageKey);
        if (!saved) return;
        
        const data = JSON.parse(saved);
        Object.keys(data).forEach(key => {
            const input = this.form.elements[key];
            if (input) input.value = data[key];
        });
    }
    
    clearFormData() {
        localStorage.removeItem(this.storageKey);
    }
}

// Usage
new FormPersistence('contactForm');

Important Considerations

Storage Limits:
  • ~5-10MB per origin (varies by browser)
  • Synchronous API (blocks main thread)
  • String-only storage
Security:
  • Not encrypted
  • Accessible via JavaScript
  • Don't store passwords or tokens
  • Vulnerable to XSS attacks
  • Shared across all scripts on origin

Best Practices

1. Always use try/catch
try {
    localStorage.setItem('key', value);
} catch (error) {
    if (error.name === 'QuotaExceededError') {
        console.error('Storage quota exceeded');
    }
}
2. Validate data before storing
function safeStore(key, value) {
    if (!key || value === undefined) return false;
    try {
        localStorage.setItem(key, JSON.stringify(value));
        return true;
    } catch {
        return false;
    }
}
3. Use sessionStorage for temporary data
// Persists only for session
sessionStorage.setItem('tempData', 'value');

// Cleared when tab closes

Key Takeaways