Chapter 5: Browser Storage & Caching
Modern web applications require robust client-side persistence for performance, offline functionality, and state management. This chapter provides a technical reference for the Web Storage API, IndexedDB, and the Cache API, along with engineering standards for storage orchestration.
I. The Storage Hierarchy
Browsers provide a tiered storage architecture. Selecting the correct mechanism is critical for performance and data integrity.
| Mechanism | API Type | Capacity | Lifecycle | Use Case |
|---|---|---|---|---|
| SessionStorage | Synchronous | ~5MB | Tab session | Temporary UI state, single-session forms. |
| LocalStorage | Synchronous | ~5MB | Persistent | User preferences, theme settings, offline drafts. |
| IndexedDB | Asynchronous | Large (Disk %) | Persistent | Structured data, binary blobs, large datasets. |
| Cache API | Asynchronous | Large (Disk %) | Persistent | Network requests/responses, offline assets (PWA). |
II. Web Storage (Local & Session)
The Web Storage API provides a simple key-value pair mechanism. Both localStorage and sessionStorage implement the Storage interface.
1. API Reference: Storage Interface
| Method | Syntax | Return | Description |
|---|---|---|---|
setItem() | storage.setItem(k, v) | void | Adds or updates a key with a string value. |
getItem() | storage.getItem(k) | string|null | Retrieves the value associated with the key. |
removeItem() | storage.removeItem(k) | void | Deletes the specified key/value pair. |
clear() | storage.clear() | void | Deletes all keys for the current origin. |
key() | storage.key(index) | string|null | Returns the name of the key at the specified index. |
2. Multi-Tab Synchronization: The storage Event
localStorage fires an event on other windows/tabs of the same origin when data is modified.
window.addEventListener('storage', (event) => {
console.log(`Storage Update:`);
console.log(`Key: ${event.key}`);
console.log(`Old Value: ${event.oldValue}`);
console.log(`New Value: ${event.newValue}`);
console.log(`Source URL: ${event.url}`);
});
Note: This event does not fire in the tab that performed the modification.
III. IndexedDB (Transactional Database)
IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. It uses indexes to enable high-performance searches.
1. Technical Workflow
- Open Database:
indexedDB.open(name, version) - Handle Upgrades: Create
objectStoresinonupgradeneeded. - Start Transaction: Create a transaction (
readonlyorreadwrite). - Perform Operations:
add,put,get,delete.
2. Implementation: Robust Storage Pattern
const openDB = (dbName, version) => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(dbName, version);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains('courses')) {
db.createObjectStore('courses', { keyPath: 'id', autoIncrement: true });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};
const saveCourse = async (course) => {
const db = await openDB('ApplicationDB', 1);
const tx = db.transaction('courses', 'readwrite');
const store = tx.objectStore('courses');
return new Promise((resolve, reject) => {
const request = store.put(course);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};
IV. The Cache API
The Cache API provides a storage mechanism for Request / Response object pairs. It is the core of Service Worker-based offline capabilities.
1. API Reference: Cache Interface
| Method | Syntax | Return | Description |
|---|---|---|---|
match() | cache.match(req) | Promise<Response|null> | Returns the first matching response. |
add() | cache.add(url) | Promise<void> | Fetches the URL and stores the response. |
put() | cache.put(req, res) | Promise<void> | Manually stores a specific request/response pair. |
delete() | cache.delete(req) | Promise<boolean> | Deletes the matching entry. |
keys() | cache.keys() | Promise<Array<Request>> | Returns an array of keys (requests). |
V. Storage Management (Quotas & Persistence)
Browsers manage storage through quotas and eviction policies.
1. Estimating Storage
Use navigator.storage.estimate() to determine how much space is available.
if (navigator.storage && navigator.storage.estimate) {
const { usage, quota } = await navigator.storage.estimate();
const percentUsed = ((usage / quota) * 100).toFixed(2);
console.log(`Storage Usage: ${percentUsed}% (${usage} of ${quota} bytes)`);
}
2. Requesting Persistence
By default, storage is "best-effort" (browser can evict if disk is full). You can request "persistent" storage.
if (navigator.storage && navigator.storage.persist) {
const isPersisted = await navigator.storage.persist();
console.log(`Persistence granted: ${isPersisted}`);
}
VI. Core Engineering Standards
1. Performance Mandates
- Async Preference: Use
IndexedDBorCache APIfor datasets larger than 1MB.LocalStorageis synchronous and will block the main thread, causing frame drops (jank). - JSON Serialization: Always wrap
JSON.parseandJSON.stringifyintry/catchblocks when accessing Web Storage to prevent crashes on corrupted data.
2. Security Mandates
- No Secrets: NEVER store PII (Personally Identifiable Information), passwords, or high-privilege JWTs in browser storage. Browser storage is accessible via XSS. Use
HttpOnlycookies for session identifiers. - Quota Management: Always handle
QuotaExceededErrorgracefully. Implement a cleanup strategy (e.g., LRU cache) for application data.