JavaScript Browser Storage Explained — localStorage, sessionStorage, IndexedDB & Cookies
Browser storage is how web applications persist data on the user’s device — from simple preferences to complex offline databases. Every web app you use relies on one or more storage mechanisms. The Doda Browser uses localStorage for user preferences, IndexedDB for cached bookmarks, and the Cache API for offline resources.
What You’ll Learn
localStorageandsessionStorage— key-value storage with different lifetimes- Cookies — HTTP-only, SameSite, Secure flags, and when to use them
- IndexedDB — a full NoSQL database in the browser
- Cache API — programmatic caching for offline support
- Storage limits, quotas, and security best practices
- A real-world offline form data example using IndexedDB
Why Browser Storage Matters
Without browser storage, every page refresh would log users out, every setting would reset, and every preference would be forgotten. The Durga Antivirus Pro web dashboard uses IndexedDB to store scan history locally so users can browse past results even when offline. Understanding storage options helps you choose the right tool for each job.
Learning Path
flowchart LR
A[JS DOM & Browser APIs] --> B[Browser Storage]
B --> C[Fetch API]
B --> D[Performance]
B --> E[You Are Here]
localStorage
localStorage stores key-value pairs that persist even after the browser is closed:
// Store data
localStorage.setItem('theme', 'dark');
localStorage.setItem('fontSize', '16');
// Retrieve data
const theme = localStorage.getItem('theme');
console.log(theme); // 'dark'
// Remove a key
localStorage.removeItem('fontSize');
// Clear all data
// localStorage.clear();
// Get count of items
console.log(localStorage.length); // 1
Key facts:
- Stores only strings (use
JSON.stringify/JSON.parsefor objects) - Persists until explicitly deleted
- Limit: ~5-10 MB per origin
- Synchronous — blocks the main thread
Storing Objects
const userPrefs = {
theme: 'dark',
fontSize: 14,
notifications: true
};
// Save
localStorage.setItem('prefs', JSON.stringify(userPrefs));
// Read
const savedPrefs = JSON.parse(localStorage.getItem('prefs'));
console.log(savedPrefs.theme); // 'dark'
sessionStorage
sessionStorage works just like localStorage but data is cleared when the tab is closed:
// Save tab-specific state
sessionStorage.setItem('scrollPosition', '450');
sessionStorage.setItem('formDraft', JSON.stringify({
name: 'Alice',
message: 'Draft text'
}));
// Restore on page refresh
const position = sessionStorage.getItem('scrollPosition');
if (position) {
window.scrollTo(0, parseInt(position));
}When to use: Tab-scoped data like form drafts, temporary selections, or multi-step wizard state that shouldn’t survive a tab close.
Cookies
Cookies are small pieces of data sent with every HTTP request to the same origin:
// Set a cookie
document.cookie = 'sessionId=abc123; path=/; max-age=86400; Secure; SameSite=Strict';
// Read all cookies
console.log(document.cookie); // 'sessionId=abc123; theme=dark'
// Set a cookie with options
function setCookie(name, value, days) {
const expires = new Date(Date.now() + days * 864e5).toUTCString();
document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/; SameSite=Lax`;
}
// Delete a cookie
document.cookie = 'sessionId=; max-age=0; path=/';Cookie Flags
| Flag | Purpose |
|---|---|
Secure | Only sent over HTTPS |
HttpOnly | Not accessible via JavaScript (prevents XSS) |
SameSite=Strict | Only sent for same-site requests |
SameSite=Lax | Sent for top-level navigation from other sites |
SameSite=None | Sent for cross-site requests (requires Secure) |
Security insight: Always use HttpOnly for session cookies — it prevents XSS attacks from stealing the cookie. Use Secure to prevent transmission over HTTP. The Durga Antivirus Pro dashboard sets HttpOnly and Secure on all authentication cookies.
IndexedDB
IndexedDB is a full NoSQL database built into the browser:
// Open a database
const request = indexedDB.open('AppDatabase', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
const store = db.createObjectStore('notes', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('title', 'title', { unique: false });
};
request.onsuccess = (event) => {
const db = event.target.result;
console.log('Database ready');
};
// Add data
function addNote(db, note) {
const transaction = db.transaction('notes', 'readwrite');
const store = transaction.objectStore('notes');
const request = store.add(note);
request.onsuccess = () => console.log('Note saved');
request.onerror = () => console.error('Save failed');
}
// Read data
function getAllNotes(db) {
return new Promise((resolve, reject) => {
const transaction = db.transaction('notes', 'readonly');
const store = transaction.objectStore('notes');
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}Key facts:
- Stores structured data (objects, files, blobs)
- Asynchronous API — doesn’t block the main thread
- Limit: Usually 50% of available disk space (can be GBs)
- Supports indexes for fast queries
Using a Promise Wrapper
IndexedDB’s event-based API is verbose. Here’s a simpler wrapper:
function openDB(name, version, upgradeFn) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(name, version);
request.onupgradeneeded = (e) => upgradeFn(e.target.result);
request.onsuccess = (e) => resolve(e.target.result);
request.onerror = (e) => reject(e.target.error);
});
}Cache API
The Cache API is part of the Service Worker specification and stores HTTP responses:
// Open a cache and add resources
const cache = await caches.open('app-v1');
await cache.addAll([
'/',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
]);
// Check cache for a request
const response = await caches.match('/images/logo.png');
if (response) {
const blob = await response.blob();
// Use the cached image
}
// Delete old cache
await caches.delete('app-v1');Best for: Offline support, caching API responses, and progressive web apps.
Storage Limits
| Storage Type | Typical Limit | Persistence |
|---|---|---|
| localStorage | 5-10 MB | Until deleted |
| sessionStorage | 5-10 MB | Tab close |
| Cookies | 4 KB per cookie, ~50 per domain | Per expiry |
| IndexedDB | ~50% of disk space | Until deleted |
| Cache API | ~50% of disk space | Until deleted |
Common Mistakes
1. Storing sensitive data in localStorage
localStorage.setItem('token', 'eyJhbGci...'); // ❌ XSS vulnerable
Fix: Use HttpOnly cookies for sensitive data. localStorage is accessible to any JavaScript on the page.
2. Forgetting JSON parse/stringify
localStorage.setItem('user', { name: 'Alice' }); // Saves "[object Object]"
Fix: Always use JSON.stringify to store and JSON.parse to retrieve objects.
3. Synchronous storage calls in performance-critical paths
localStorage.getItem() blocks the main thread. Avoid it in tight loops or animations.
4. Not checking quota limits
try {
localStorage.setItem('key', 'largeData');
} catch (e) {
if (e.name === 'QuotaExceededError') {
console.log('Storage full — clear old data');
}
}Fix: Always wrap in try/catch and handle QuotaExceededError.
5. Mixing SameSite cookie settings
Setting SameSite=None without Secure causes modern browsers to reject the cookie.
Practice Questions
What’s the difference between localStorage and sessionStorage? localStorage persists until explicitly deleted. sessionStorage is cleared when the tab or browser is closed.
When should you use IndexedDB instead of localStorage? For large or complex data (megabytes+), structured data needing indexes, and offline-capable applications.
What does HttpOnly do on a cookie? It prevents JavaScript from accessing the cookie, protecting against XSS attacks that would steal session tokens.
How does the Cache API differ from IndexedDB? Cache API stores HTTP Request/Response pairs. IndexedDB stores arbitrary JavaScript objects. Cache API is designed for offline resource caching, IndexedDB for application data.
Challenge: Build a function that detects available storage and returns the best storage method for a given data size.
Mini Project: Offline Form Data with IndexedDB
class OfflineForm {
constructor(dbName = 'formDB') {
this.dbPromise = openDB(dbName, 1, (db) => {
const store = db.createObjectStore('submissions', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('synced', 'synced', { unique: false });
});
}
async save(formData) {
const db = await this.dbPromise;
const tx = db.transaction('submissions', 'readwrite');
const store = tx.objectStore('submissions');
await store.add({ ...formData, synced: false, timestamp: Date.now() });
}
async getUnsynced() {
const db = await this.dbPromise;
const tx = db.transaction('submissions', 'readonly');
const store = tx.objectStore('submissions');
const index = store.index('synced');
return index.getAll(IDBKeyRange.only(false));
}
async markSynced(id) {
const db = await this.dbPromise;
const tx = db.transaction('submissions', 'readwrite');
const store = tx.objectStore('submissions');
const item = await store.get(id);
item.synced = true;
await store.put(item);
}
}
// Usage
(async () => {
const form = new OfflineForm();
await form.save({ name: 'Alice', message: 'Hello offline!' });
const unsynced = await form.getUnsynced();
console.log(`Pending submissions: ${unsynced.length}`);
})();FAQ
What’s Next
| Lesson | Description |
|---|---|
| JavaScript Home | Back to the JavaScript hub |
| https://tutorials.dodatech.com/programming-languages/javascript/js-dom/ | DOM & Browser APIs |
| https://tutorials.dodatech.com/programming-languages/javascript/js-web-apis/ | Web APIs — notifications, workers, observers |
| https://tutorials.dodatech.com/programming-languages/javascript/js-performance/ | JavaScript performance optimization |
| PWA | Progressive Web Apps |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
What’s Next
Congratulations on completing this JavaScript Browser Storage tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro