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.
What estimate() returns
Section titled “What estimate() returns”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 ofusageper 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.
How the quota is computed
Section titled “How the quota is computed”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.
Best-effort by default
Section titled “Best-effort by default”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.
Handling a full quota
Section titled “Handling a full quota”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; } }}Practical checklist
Section titled “Practical checklist”- Feature-detect
navigator.storageandnavigator.storage.estimatebefore calling; older browsers lack the StorageManager API. - Call
estimate()before large writes and compareusageagainstquota, pruning stale caches when you are near the ceiling. - Treat
usage/quotaas rounded estimates — leave headroom rather than writing right up to the reportedquota. - Handle
QuotaExceededErroron Cache writes (a rejected promise) and on IndexedDB writes (a transactionerror/abortevent); 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.
What this means for trust
Section titled “What this means for trust”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.