Storage · API
StorageManager.estimate()
Published
navigator.storage.estimate() resolves to the number of bytes this origin already stores
(usage) and a conservative ceiling on how many it may store (quota). The ceiling is a share
of the disk computed by the browser at call time, not a constant, so it is read at runtime
rather than hardcoded.
Syntax
Section titled “Syntax”navigator.storage.estimate()The method takes no arguments and returns a Promise that fulfils with a StorageEstimate
dictionary. It is available in windows and in workers (self.navigator.storage inside a service
worker), and only in secure contexts (BCD api.StorageManager.estimate: Chrome 61, Firefox 57,
Safari 17).
Parameters
Section titled “Parameters”None.
Return value
Section titled “Return value”The promise fulfils with an object carrying two standard members and one Chromium extension.
| Member | Type | Meaning |
|---|---|---|
usage |
number | Bytes the origin occupies across the quota-managed stores (Cache Storage, IndexedDB, OPFS, service worker registrations). Rounded and padded, so not an exact ledger. |
quota |
number | Bytes the origin may use in total. A conservative estimate that shrinks as the disk fills. |
usageDetails |
object | Chromium only (BCD api.StorageManager.estimate.usageDetails). A breakdown of usage by storage system; stores with zero usage are omitted, so test for each key before reading it. |
The Storage Standard lets the browser obfuscate both numbers with compression, deduplication,
and padding so that a page cannot infer disk size or another site’s data from exact byte counts.
Chromium’s padding of opaque responses is the most visible case: a cross-origin no-cors
response of a few kilobytes contributes roughly 7 MB to usage (Understanding storage
quota,
developer.chrome.com).
How the quota is sized
Section titled “How the quota is sized”Each engine derives quota from disk size, and the formulas differ (Storage quotas and
eviction criteria,
developer.mozilla.org).
| Engine | Per-origin quota | Ceiling across all origins |
|---|---|---|
| Chromium (Chrome, Edge) | 60 % of total disk size, best-effort or persistent alike. Incognito reduces this to about 5 %, and the “Clear cookies and site data when you close all windows” setting caps it near 300 MB (Storage for the web, web.dev). | 80 % of total disk |
| Firefox | The smaller of 10 % of total disk and the 10 GiB group limit shared by all origins of one site (eTLD+1). Persistent origins may use 50 % of disk, capped at 8 TiB, outside the group limit. | 50 % of free disk |
| WebKit (Safari 17, iOS 17, macOS 14) | About 60 % of total disk in a browser app, about 15 % inside another app’s WKWebView; a cross-origin frame gets 10 % of the embedding origin’s quota. A Home Screen or Dock web app keeps the browser figure (Updates to Storage Policy, webkit.org). |
80 % of disk in a browser app, 20 % in other apps |
Before Safari 17 an origin started with 1 GiB and Safari asked the user before granting more,
in 200 MB steps. The quota an older WebKit reports therefore moves after a prompt, which is
one more reason to re-read it before each large write rather than caching it.
Exceptions
Section titled “Exceptions”| Exception | When |
|---|---|
TypeError |
The browser cannot obtain a storage shelf for the origin: the origin is opaque (a sandboxed <iframe> without allow-same-origin, a data: document) or the user has disabled storage for the site. The promise rejects; the method itself does not throw. |
Running out of room is not an exception of estimate(). Writes that exceed quota fail in the
store that performed them: cache.put() rejects with a QuotaExceededError DOMException,
and an IndexedDB request fires error with request.error.name === "QuotaExceededError" and
aborts its transaction.
Examples
Section titled “Examples”The examples read the estimate for display, use it to decide before a large write, and handle its absence.
Reading the estimate and showing a budget line
Section titled “Reading the estimate and showing a budget line”The two members are enough to show how full the origin is. Divide before formatting, because
both values are bytes and quota can exceed 2^53 bytes only on absurdly large disks, so plain
floating-point arithmetic is fine.
const { usage, quota } = await navigator.storage.estimate();const percent = ((usage / quota) * 100).toFixed(1);console.log(`${(usage / 1048576).toFixed(1)} MiB of ${(quota / 1073741824).toFixed(1)} GiB (${percent} %)`);Expect quota to be hundreds of gigabytes on a desktop with Chromium’s 60 % rule and far less
on a phone that is nearly full; a budget that fits one device will not fit another.
Checking headroom before a large download
Section titled “Checking headroom before a large download”Call estimate() immediately before a write you know the size of, then keep a margin for the
padding described above. If the write still fails, catch QuotaExceededError, free space, and
retry once instead of failing the whole operation.
async function cacheLargeAsset(cache, url, expectedBytes) { const { usage, quota } = await navigator.storage.estimate(); if (quota - usage < expectedBytes * 1.2) { await pruneOldEntries(cache); } try { await cache.add(url); } catch (err) { if (err.name !== 'QuotaExceededError') throw err; await pruneOldEntries(cache); await cache.add(url); }}The 20 % margin is a working allowance for rounding, not a figure from any specification; the
retry after pruning is the part that matters, because a quota read a second earlier is already
stale on a device that is also downloading photos.
Detecting support and degrading
Section titled “Detecting support and degrading”navigator.storage is absent on insecure origins and in browsers older than those listed in
the Syntax section. Treat a missing method as “no budget information” and write optimistically,
relying on the store’s own QuotaExceededError.
async function storageBudget() { if (!('storage' in navigator) || typeof navigator.storage.estimate !== 'function') { return null; // unknown budget: fall back to catching the quota error on write } try { return await navigator.storage.estimate(); } catch (err) { if (err.name === 'TypeError') return null; // opaque origin or storage disabled throw err; }}A null result keeps the calling code on one path: it attempts the write and handles the
failure, which it must do anyway because the estimate is advisory.
See also
Section titled “See also”- Storage: estimate() method (whatwg.org)
- Updates to Storage Policy (webkit.org)
- Storage quotas and eviction criteria (developer.mozilla.org)
- navigator.storage.persist()
- Eviction and best-effort storage
- Cache Storage API
Specifications
| Specification | Status |
|---|---|
| None. | |