Choosing the right storage mechanism depends on your use case. Here's the definitive comparison.
Quick Comparison
| Feature | localStorage | sessionStorage | Cookies | IndexedDB |
|---|---|---|---|---|
| Capacity | 5-10MB | 5-10MB | 4KB per cookie | GBs |
| Expiry | Never | Tab close | Configurable | Never |
| Server access | No | No | Yes (auto-sent) | No |
| API | Sync | Sync | Sync (string) | Async |
| Data type | Strings | Strings | Strings | Any structured |
| Web Workers | No | No | No | Yes |
localStorage
// Simple key-value store
localStorage.setItem("theme", "dark");
localStorage.getItem("theme"); // "dark"
localStorage.removeItem("theme");
// Objects need serialization
localStorage.setItem("user", JSON.stringify({ name: "Rahul" }));
const user = JSON.parse(localStorage.getItem("user"));Best for: User preferences, theme, non-sensitive cached data.
sessionStorage
// Same API as localStorage, but data cleared on tab close
sessionStorage.setItem("scrollPosition", "500");
// Unique per tab — opening same URL in new tab gets fresh storageBest for: Form state preservation, one-time notifications, tab-specific data.
Cookies
// Setting cookies
document.cookie = "theme=dark; max-age=31536000; path=/; SameSite=Strict";
// HttpOnly cookies (set by server only)
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Path=/
// Reading cookies (painful API)
const theme = document.cookie
.split("; ")
.find(c => c.startsWith("theme="))
?.split("=")[1];Best for: Authentication tokens (HttpOnly), server-read preferences.
Security Considerations
- localStorage/sessionStorage: Accessible via XSS. Never store auth tokens here.
- HttpOnly cookies: Not accessible via JavaScript. Best for auth tokens.
- Secure flag: Only sent over HTTPS.
- SameSite: Prevents CSRF. Use Strict or Lax.
Decision Guide
- Auth tokens → HttpOnly cookies
- User preferences → localStorage
- Form drafts → sessionStorage
- Large datasets/offline data → IndexedDB
- Data the server needs → Cookies