Storage Quotas & Eviction Policies
Modern web applications increasingly rely on client-side persistence to deliver seamless offline experiences. However, browser storage is not infinite, and the mechanisms governing allocation and cleanup are highly dynamic. For frontend engineers and PWA developers, understanding how rendering engines manage disk pressure is critical to preventing silent data loss. This guide details the heuristics behind storage allocation, cross-vendor quota variations, persistence tiers, and production-ready strategies for monitoring and handling eviction events.
Understanding Browser Storage Allocation
Modern browsers implement dynamic storage models that scale based on available disk space and user engagement metrics. Unlike legacy fixed-capacity models, contemporary Browser Storage Fundamentals & Quotas rely on heuristic algorithms to determine how much data an origin can safely persist. These algorithms factor in global device storage, origin visit frequency, and whether the application is installed as a PWA. Engineers must account for these fluid boundaries when designing offline-first architectures, treating available storage as a shared, volatile resource rather than a guaranteed allocation. Proactive quota management should be baked into the application lifecycle, not treated as an afterthought.
Cross-Browser Quota Variations
Storage limits are not standardized across rendering engines. Each vendor applies distinct heuristics, compression strategies, and background throttling rules. For a detailed breakdown of how Browser storage limits across Chrome, Firefox, and Safari differ in practice, consult our comparative analysis. Chromium-based browsers typically allocate up to ~60% of available free disk space for the temporary storage group (shared across IndexedDB, CacheStorage, and Origin Private File System), while Firefox caps the temporary storage group at approximately 20% of total disk space. Safari imposes stricter per-origin caps and aggressively purges data after seven days of user inactivity for non-installed origins. Understanding these variations is critical for cross-platform PWA reliability, especially when caching large media assets or synchronizing offline databases across mobile and desktop environments.
Eviction Tiers and Persistence Guarantees
Browsers categorize stored data into priority tiers to manage disk pressure efficiently. Synchronous key-value stores like LocalStorage vs SessionStorage fall outside the Storage API quota buckets (they are managed separately), but they are still subject to browser- and OS-level eviction. IndexedDB and the Cache API fall under the temporary storage tier by default; they can request durable storage via the StorageManager API. Properly leveraging Understanding Web Storage APIs ensures your application requests the appropriate persistence level for critical state. When persistent storage is granted, the origin is exempt from automatic eviction unless the user explicitly clears site data or the device runs completely out of disk space.
Monitoring and Debugging Eviction Events
When disk pressure mounts, browsers silently evict data from non-persistent origins. Proactive monitoring is essential. Learn how to implement quota change listeners and handle QuotaExceededError gracefully by reviewing our guide on Debugging storage eviction in progressive web apps. Concurrent read/write operations during eviction windows can corrupt state; use transactions with explicit onerror and onabort handlers on IDBTransaction to isolate and resolve conflicts. Implementing telemetry around storage usage and eviction events allows teams to trigger proactive data pruning before users encounter failures.
Production Implementation Patterns
The following patterns demonstrate how to request persistent storage, monitor quota thresholds, and gracefully handle quota exhaustion in offline-first applications.
Requesting Persistent Storage & Monitoring Quota
async function manageStorageQuota() {
if (navigator.storage && navigator.storage.persist) {
const isPersisted = await navigator.storage.persist();
console.log(`Persistent storage granted: ${isPersisted}`);
}
const { usage = 0, quota = 0 } = await navigator.storage.estimate();
const usagePercent = quota > 0 ? (usage / quota) * 100 : 0;
console.log(`Storage usage: ${usagePercent.toFixed(2)}%`);
if (usagePercent > 80) {
console.warn(
'Approaching quota limit. Implementing data pruning strategy.'
);
await pruneOldCacheEntries();
}
}
Handling QuotaExceededError Gracefully
async function saveCriticalState(db: IDBDatabase, data: Record<string, unknown>) {
return new Promise<void>((resolve, reject) => {
const tx = db.transaction('state', 'readwrite');
const store = tx.objectStore('state');
tx.oncomplete = () => resolve();
tx.onerror = () => {
const err = tx.error;
if (err && err.name === 'QuotaExceededError') {
console.error('Storage quota exceeded. Falling back to server sync.');
// Kick off background tasks without blocking the current call
syncToCloudFallback(data).then(() => clearNonEssentialCache());
}
reject(err);
};
store.put(data, 'critical_state');
});
}
Common Pitfalls & Troubleshooting
Avoid these frequent implementation mistakes that lead to data loss, degraded UX, or silent failures in offline-first architectures:
- Assuming
localStorageprovides persistent storage guarantees across browser restarts.localStorageis susceptible to eviction under disk pressure. Migrate critical offline state to IndexedDB. - Failing to handle
QuotaExceededErrorduring bulk IndexedDB writes. Always wrap bulk transactions intry/catchblocks and implement atomic rollback or fallback sync mechanisms. - Ignoring
navigator.storage.estimate()before initiating large asset downloads. Check remaining quota before fetching large media or dataset payloads to prevent mid-download failures. - Not implementing a fallback sync mechanism when persistent storage requests are denied. Browsers may deny
navigator.storage.persist()based on engagement metrics. Design your app to degrade gracefully by syncing to a remote backend or using ephemeral caching. - Writing to storage synchronously on the main thread, causing jank during eviction checks. Offload quota estimation, pruning, and large writes to Web Workers or use asynchronous IndexedDB transactions to maintain UI responsiveness.
Frequently Asked Questions
What triggers browser storage eviction? Eviction is primarily triggered by system-wide disk pressure, prolonged inactivity of an origin, or explicit user actions like clearing site data. Browsers prioritize evicting non-persistent, low-engagement origins first.
How can I prevent my PWA data from being evicted?
Call navigator.storage.persist() to request durable storage. Browsers typically grant this to PWAs with high user engagement, installed status, or active service workers. Always handle denial gracefully.
Does localStorage count toward the storage quota reported by navigator.storage.estimate()?
No. localStorage is managed separately from the Storage API quota bucket. navigator.storage.estimate() reports usage for IndexedDB, CacheStorage, and the Origin Private File System. localStorage has its own ~5 MB per-origin limit enforced independently.
How do I monitor remaining storage space programmatically?
Use the StorageManager API via navigator.storage.estimate(). It returns a Promise resolving to an object containing usage (bytes used) and quota (total bytes available) for the current origin’s temporary storage group.