localStorage
localStorage is a simple key-value storage API built into web browsers. It provides persistent storage that survives browser restarts, making it useful for storing user preferences and small amounts of application state.
Basic Operations
// Store data
localStorage.setItem('theme', 'dark');
localStorage.setItem('user', JSON.stringify({ name: 'Alice' }));
// Retrieve data
const theme = localStorage.getItem('theme');
const user = JSON.parse(localStorage.getItem('user'));
// Remove data
localStorage.removeItem('theme');
// Clear all
localStorage.clear();
// Check storage size
const used = new Blob(Object.values(localStorage)).size;
console.log(`Using ${used} bytes`);
Key Characteristics
- String only: All values stored as strings
- Synchronous: Blocks the main thread
- Per-origin: Data isolated by domain
- Persistent: Survives browser restarts
- ~5 MB limit: Varies by browser
localStorage vs sessionStorage
| Aspect | localStorage | sessionStorage |
|---|---|---|
| Persistence | Until cleared | Until tab closes |
| Scope | All tabs/windows | Single tab |
| Use case | User preferences | Form state, temp data |
localStorage vs Alternatives
| Storage | Capacity | Async | Data Types |
|---|---|---|---|
| localStorage | ~5 MB | No | Strings |
| sessionStorage | ~5 MB | No | Strings |
| IndexedDB | GBs | Yes | Any |
| Cookies | ~4 KB | No | Strings |
What We Like
- Simplicity: Dead-simple API
- Browser support: Universal availability
- Debugging: Easy to inspect in DevTools
- No setup: Works immediately
What We Don't Like
- Synchronous: Can block UI on large reads/writes
- Strings only: Must serialize/deserialize objects
- No encryption: Data visible in DevTools
- Limited capacity: 5 MB fills quickly
- No expiration: Must implement manually
Common Use Cases
- User preferences (theme, language)
- Form draft autosave
- Feature flags and A/B test assignments
- Authentication tokens (with caveats)
- Cache for small datasets
Security Considerations
// DON'T store sensitive data unencrypted
localStorage.setItem('password', userPassword); // Bad!
// DO use for non-sensitive preferences
localStorage.setItem('theme', 'dark'); // Fine
// For auth tokens, prefer:
// - HttpOnly cookies (server-set)
// - Memory + short-lived tokens
// - Encrypted storage libraries
Best Practices
- Handle quota errors: Wrap in try-catch
- Use a wrapper: Abstract JSON parsing
- Clean up: Remove stale data
- Don't store secrets: Use secure alternatives
- Consider IndexedDB: For larger or complex data
Simple Wrapper
const storage = {
get(key, defaultValue = null) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch {
return defaultValue;
}
},
set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch {
return false;
}
},
remove(key) {
localStorage.removeItem(key);
},
};