Example: Storage event for cross-tab communication
// Listen for storage changes from other tabswindow.addEventListener("storage", (event) => { console.log("Storage changed in another tab"); 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 === localStorage); // React to specific changes if (event.key === "theme") { applyTheme(event.newValue); } // Handle logout in all tabs if (event.key === "isLoggedIn" && event.newValue === "false") { redirectToLogin(); }});// Trigger storage event in other tabslocalStorage.setItem("notification", JSON.stringify({ "message": "New update available", "timestamp": Date.now()}));
Note: Web Storage only stores strings - use JSON.stringify() and JSON.parse() for objects. Storage is synchronous and can block UI - use IndexedDB for large data. Data is scoped to origin (protocol + domain + port). Storage event doesn't fire in the tab that made the change.
Warning: Storage can throw QuotaExceededError when full - always wrap in try/catch. Private browsing modes may have reduced or disabled storage. Never store sensitive data (passwords, tokens) in localStorage - vulnerable to XSS. Use sessionStorage for temporary data that shouldn't persist.
2. IndexedDB Database Operations and Transactions
Method
Syntax
Description
Browser Support
open
indexedDB.open(name, version)
Opens database. Returns IDBOpenDBRequest. Triggers upgradeneeded if version changes.
Note: IndexedDB is asynchronous and event-based. Schema changes (createObjectStore, createIndex) only allowed in upgradeneeded event. Use put() for upsert (insert or update), add() fails if key exists. IndexedDB can store large amounts of data (hundreds of MB to GB depending on browser).
Warning: All IndexedDB operations are transactional. Transaction auto-commits when all requests complete. Don't perform async operations (fetch, setTimeout) inside transaction handlers - transaction will close. Use indexes for queries - full table scans with cursors are slow on large datasets.
3. Cache API for Service Worker Integration
Method
Syntax
Description
Browser Support
caches.open
caches.open(cacheName)
Opens named cache. Creates if doesn't exist. Returns Promise<Cache>.
Modern Browsers
caches.match
caches.match(request, options)
Searches all caches for matching request. Returns Promise<Response> or undefined.
Modern Browsers
caches.has
caches.has(cacheName)
Checks if named cache exists. Returns Promise<boolean>.
Modern Browsers
caches.delete
caches.delete(cacheName)
Deletes named cache. Returns Promise<boolean> indicating if cache existed.
Modern Browsers
caches.keys
caches.keys()
Returns Promise with array of cache names.
Modern Browsers
Cache Method
Description
Use Case
add
Fetches URL and stores response. Single request.
Cache single resource
addAll
Fetches array of URLs and stores responses. Atomic - fails if any fetch fails.
Cache multiple resources
put
Stores request-response pair directly. Full control over what's cached.
Cache custom responses
match
Searches cache for matching request. Returns Promise<Response> or undefined.
Retrieve cached response
matchAll
Returns all matching responses. Optional request parameter for filtering.
Returns all request keys in cache. Optional request parameter for filtering.
List cached URLs
Example: Basic Cache API usage
// Open cache and add resourcesasync function cacheResources() { const cache = await caches.open("my-cache-v1"); // Add single resource await cache.add("/api/data"); // Add multiple resources (atomic operation) await cache.addAll([ "/", "/styles.css", "/script.js", "/logo.png" ]); console.log("Resources cached");}// Check cache before fetchingasync function fetchWithCache(url) { // Try cache first const cachedResponse = await caches.match(url); if (cachedResponse) { console.log("Cache hit:", url); return cachedResponse; } // Cache miss - fetch from network console.log("Cache miss:", url); const response = await fetch(url); // Store in cache for next time const cache = await caches.open("my-cache-v1"); cache.put(url, response.clone()); return response;}// Usageconst response = await fetchWithCache("/api/users");const data = await response.json();
Example: Cache strategies
// Cache First (good for static assets)async function cacheFirst(request) { const cached = await caches.match(request); if (cached) return cached; const response = await fetch(request); const cache = await caches.open("static-v1"); cache.put(request, response.clone()); return response;}// Network First (good for dynamic data)async function networkFirst(request) { try { const response = await fetch(request); const cache = await caches.open("dynamic-v1"); cache.put(request, response.clone()); return response; } catch (error) { const cached = await caches.match(request); if (cached) return cached; throw error; }}// Stale While Revalidate (best of both)async function staleWhileRevalidate(request) { const cached = await caches.match(request); const fetchPromise = fetch(request).then((response) => { const cache = caches.open("swr-v1"); cache.then((c) => c.put(request, response.clone())); return response; }); return cached || fetchPromise;}// Cache with expirationasync function cacheWithExpiry(request, cacheName, maxAge) { const cache = await caches.open(cacheName); const cached = await cache.match(request); if (cached) { const cachedDate = new Date(cached.headers.get("date")); const age = Date.now() - cachedDate.getTime(); if (age < maxAge) { return cached; // Still fresh } } // Expired or missing - fetch fresh const response = await fetch(request); cache.put(request, response.clone()); return response;}
Example: Cache management
// List all cachesasync function listCaches() { const cacheNames = await caches.keys(); console.log("Caches:", cacheNames); return cacheNames;}// Delete old cache versionsasync function deleteOldCaches(currentVersion) { const cacheNames = await caches.keys(); const deletePromises = cacheNames .filter((name) => name !== currentVersion) .map((name) => caches.delete(name)); await Promise.all(deletePromises); console.log("Old caches deleted");}// Usage in Service Worker activate eventself.addEventListener("activate", (event) => { event.waitUntil( deleteOldCaches("my-cache-v2") );});// List cached URLsasync function listCachedUrls(cacheName) { const cache = await caches.open(cacheName); const requests = await cache.keys(); const urls = requests.map((req) => req.url); console.log("Cached URLs:", urls); return urls;}// Remove specific cached itemasync function removeCachedItem(url) { const cache = await caches.open("my-cache-v1"); const deleted = await cache.delete(url); console.log(`Cache entry deleted: ${deleted}`);}// Clear all cachesasync function clearAllCaches() { const cacheNames = await caches.keys(); await Promise.all( cacheNames.map((name) => caches.delete(name)) ); console.log("All caches cleared");}
Note: Cache API stores Request-Response pairs, not just data. Primarily used with Service Workers for offline functionality. Responses must be clone()d before caching because Response body can only be read once. Cache is persistent and separate from HTTP cache.
4. Cookie Manipulation with document.cookie
Property
Description
Format
document.cookie
Gets all cookies as semicolon-separated string. Setting adds/updates single cookie.
"name=value; name2=value2"
Cookie Attribute
Description
Example
expires
Expiration date (GMT format). Cookie deleted after this date.
expires=Wed, 01 Jan 2025 00:00:00 GMT
max-age
Lifetime in seconds. Overrides expires. Negative value deletes cookie.
max-age=3600
path
URL path where cookie is accessible. Defaults to current path.
path=/
domain
Domain where cookie is accessible. Includes subdomains if specified.
domain=.example.com
secure
Cookie only sent over HTTPS. Essential for sensitive data.
secure
httpOnly
Cookie inaccessible to JavaScript (document.cookie). Only server-side. Set by server.
// Set cookiefunction setCookie(name, value, days = 7, options = {}) { let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`; // Expiration if (days) { const date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); cookie += `; expires=${date.toUTCString()}`; } // Path (default to root) cookie += `; path=${options.path || "/"}`; // Domain if (options.domain) { cookie += `; domain=${options.domain}`; } // Secure if (options.secure) { cookie += "; secure"; } // SameSite if (options.sameSite) { cookie += `; samesite=${options.sameSite}`; } document.cookie = cookie;}// Get cookiefunction getCookie(name) { const cookies = document.cookie.split("; "); for (let cookie of cookies) { const [cookieName, cookieValue] = cookie.split("="); if (decodeURIComponent(cookieName) === name) { return decodeURIComponent(cookieValue); } } return null;}// Delete cookiefunction deleteCookie(name, options = {}) { setCookie(name, "", -1, options);}// Get all cookies as objectfunction getAllCookies() { const cookies = {}; document.cookie.split("; ").forEach((cookie) => { const [name, value] = cookie.split("="); cookies[decodeURIComponent(name)] = decodeURIComponent(value); }); return cookies;}// UsagesetCookie("username", "john_doe", 30, { "path": "/", "secure": true, "sameSite": "Strict"});const username = getCookie("username");console.log(username); // "john_doe"deleteCookie("username");
Note: Cookies have 4KB size limit per cookie. Always use encodeURIComponent() for names and values. HttpOnly cookies can't be accessed via JavaScript - only set by server. Use SameSite=Strict or Lax for CSRF protection.
Warning: Cookies are sent with every HTTP request to the domain - impacts performance. Don't store sensitive data in client-accessible cookies. Secure flag is mandatory for SameSite=None. Deleting cookie requires matching path and domain of original cookie.
5. Origin Private File System API
Method
Syntax
Description
Browser Support
getDirectory NEW
navigator.storage.getDirectory()
Returns Promise<FileSystemDirectoryHandle> for origin's private file system root.
Modern Browsers
getFileHandle
dir.getFileHandle(name, options)
Gets file handle. Options: create: true to create if missing.
Modern Browsers
getDirectoryHandle
dir.getDirectoryHandle(name, options)
Gets subdirectory handle. Options: create: true to create if missing.
Modern Browsers
removeEntry
dir.removeEntry(name, options)
Deletes file or directory. Options: recursive: true for directories.
Modern Browsers
getFile
fileHandle.getFile()
Returns Promise<File> object with file data.
Modern Browsers
createWritable
fileHandle.createWritable()
Returns Promise<FileSystemWritableFileStream> for writing.
Note: Origin Private File System is private to origin and not accessible to user. Good for app-specific data, cache, temporary files. Not for user documents - use File System Access API for that. Storage is persistent and survives browser restarts.
6. Storage Quota and Usage Estimation
Method
Syntax
Description
Browser Support
estimate
navigator.storage.estimate()
Returns Promise with quota and usage info. Properties: quota, usage.
Modern Browsers
persist
navigator.storage.persist()
Requests persistent storage (won't be cleared under pressure). Returns Promise<boolean>.
Modern Browsers
persisted
navigator.storage.persisted()
Checks if storage is persistent. Returns Promise<boolean>.
Note: Quota varies by browser and available disk space (typically 10-50% of available disk). persist() requires user interaction or installed PWA. Persistent storage won't be cleared under storage pressure. Usage includes IndexedDB, Cache API, and File System API data.
Storage API Best Practices
Use localStorage for small, simple data (settings, preferences) - synchronous and limited to 5-10MB
Use IndexedDB for large structured data (offline databases, cached content) - asynchronous and scalable
Use Cache API with Service Workers for offline-first applications and resource caching
Always wrap storage operations in try/catch - quota exceeded and private mode can cause errors
Use JSON.stringify/parse for complex objects in localStorage
Set appropriate cookie attributes: Secure, HttpOnly, SameSite
Request persistent storage for critical data with navigator.storage.persist()
Monitor quota usage with navigator.storage.estimate() before large operations
Never store sensitive data (passwords, tokens) in client-side storage - vulnerable to XSS
Consider data expiration strategies for cached content to prevent stale data