Skip to content

Quotas and StorageManager.estimate()

In one line: navigator.storage.estimate() returns a promise resolving to { usage, quota } in bytes — how much this origin is already storing and a conservative estimate of how much it may use in total. The quota is a fraction of disk space — browser-specific and dynamic, not a fixed number you can hardcode.

estimate() is part of the StorageManager API, reached through navigator.storage. It resolves to an object with two standard fields, both in bytes:

const { usage, quota } = await navigator.storage.estimate();
console.log(`Using ${usage} of ~${quota} bytes`);
// estimate() may also include a usageDetails breakdown by storage system;
// storage systems with zero usage are omitted from it:
const est = await navigator.storage.estimate();
console.log(est.usageDetails);
  • usage — bytes the origin currently occupies across the quota-managed storage APIs (Cache Storage, IndexedDB, and friends).
  • quota — the total the browser estimates the origin may use. Treat it as a ceiling that can move down, not a guaranteed allocation.
  • usageDetails — a breakdown of usage per storage system (for example IndexedDB and Cache Storage); storage systems with zero usage are omitted from it. Guard for its presence before reading it.

The returned figures are intentionally imprecise. The spec lets browsers obfuscate them — through compression, deduplication, and padding — for security reasons, so a page cannot use exact byte counts to fingerprint the device or infer what a user has stored. Read usage and quota as guidance for budgeting, not as an audited ledger.

The quota is managed per origin — some browsers partition it further for third-party (cross-site) contexts — and is a fraction of disk space, so it is dynamic and browser-specific rather than a fixed allocation. The details differ between engines and move between releases:

  • The share can be large — a sizeable fraction of the disk — so on a roomy device the practical ceiling is often gigabytes, and far less on a nearly-full one.
  • Engines differ in what they measure: some base the limit on total disk size, others on free space. Same-site origins (eTLD+1) may also share a group limit so one site cannot starve its siblings.
  • Some browsers prompt or grant more room as a site proves it is worth keeping, rather than exposing one fixed number up front.

Because the exact percentages differ between engines and change between releases, never hardcode a size budget. Call estimate() at runtime and adapt to the quota the current browser reports.

An origin’s storage is best-effort unless it has been promoted to persistent via navigator.storage.persist(). Best-effort data counts against the same quota but can be evicted under storage pressure — the browser reclaims space by clearing whole origins, least-recently-used first. estimate() tells you how much room you have; it does not protect what you have stored. See persistence, quotas, and eviction for how to request durable storage.

When a write would exceed the quota, the browser raises a QuotaExceededError (a DOMException). The Cache API rejects the write promise with it; IndexedDB instead aborts the transaction and surfaces the error on the request’s or transaction’s error event rather than as a promise rejection. Guard large writes on both:

async function safePut(cache, request, response) {
try {
await cache.put(request, response);
} catch (err) {
if (err.name === 'QuotaExceededError') {
await pruneOldEntries(cache); // free space, then retry
await cache.put(request, response);
} else {
throw err;
}
}
}
  • Feature-detect navigator.storage and navigator.storage.estimate before calling; older browsers lack the StorageManager API.
  • Call estimate() before large writes and compare usage against quota, pruning stale caches when you are near the ceiling.
  • Treat usage/quota as rounded estimates — leave headroom rather than writing right up to the reported quota.
  • Handle QuotaExceededError on Cache writes (a rejected promise) and on IndexedDB writes (a transaction error/abort event); evict old data and retry instead of failing the operation.
  • Guard for usageDetails’ presence before reading it; storage systems with zero usage are omitted from the breakdown.
  • For data that must survive eviction, request persistence; estimate() alone does not make storage durable.

estimate() is the honest budget line for an offline-capable PWA: it tells you, per device and per moment, how much room the browser estimates you have before it starts pushing back. Reading it before big writes — and handling QuotaExceededError gracefully when the ceiling drops — is the difference between an app that degrades cleanly on a full disk and one that silently fails to cache the very assets a user installed it to keep offline.