Capabilities · API
Storage Buckets API
Published Updated
navigator.storageBuckets.open(name, options) creates or opens a named storage bucket: a partition of the origin’s storage with its own indexedDB factory, caches, bucket file system, persistence mode, quota ceiling, and expiry date, so the browser can evict a low-value bucket under storage pressure while leaving a persisted one intact. Without buckets an origin has a single default bucket and eviction is all-or-nothing.
Chrome 122 and Edge 122 ship StorageBucketManager and every StorageBucket member (indexedDB, caches, getDirectory(), persist(), estimate(), setExpires()) together; Chrome for Android, Samsung Internet, and Android WebView mirror that version. Firefox has not implemented it (Bugzilla 1594740) and Safari has no implementation (BCD api.StorageBucketManager). Treat buckets as an enhancement over the default bucket: a page that stores data in bucket.indexedDB on Chrome and in window.indexedDB elsewhere keeps the same schema code.
Syntax
Section titled “Syntax”navigator.storageBuckets.open(name)navigator.storageBuckets.open(name, options)navigator.storageBuckets.keys()navigator.storageBuckets.delete(name)
bucket.persist()bucket.persisted()bucket.estimate()bucket.setExpires(timestamp)bucket.expires()bucket.getDirectory()open() returns a Promise<StorageBucket>, keys() a Promise<sequence<DOMString>> of live bucket names, and delete() a Promise<undefined> that resolves even when no bucket of that name exists. navigator.storageBuckets is [SecureContext] and exposed on Window and workers; persist() is Window-only because it may consult the persistent-storage permission. The indexedDB and caches attributes are ordinary IDBFactory and CacheStorage objects scoped to the bucket, and getDirectory() returns a FileSystemDirectoryHandle for a bucket-scoped origin private file system.
Parameters
Section titled “Parameters”open() takes a bucket name and an optional StorageBucketOptions dictionary. The name must be 1 to 64 code points of ASCII lowercase letters, digits, _, or -, and must not start with _ or -; opening an existing name returns the existing bucket and ignores options.
| Member | Type | Required | Description |
|---|---|---|---|
persisted |
boolean |
No, defaults to false |
Request the "persistent" bucket mode. Granted only when the persistent-storage permission is "granted"; otherwise the bucket opens in best-effort mode and bucket.persisted() resolves false. |
quota |
unsigned long long (bytes) |
No | Upper bound for the bucket’s usage; the browser may enforce a lower one. bucket.estimate() reports it as quota when set. |
expires |
DOMHighResTimeStamp (ms since the Unix epoch) |
No | When the bucket is to be treated as gone. An expired bucket is removed lazily on the next open(), keys(), or access. |
Chromium’s IDL also carries a durability member ("strict" or "relaxed") behind the StorageBucketsDurability runtime flag; the WICG explainer removed it from the first version of the API because IndexedDB transactions already expose the same durability option, so do not rely on it.
The StorageBucket returned by open() has these members.
| Member | Type | Description |
|---|---|---|
name |
DOMString (read-only) |
The key the bucket was opened with. |
persist() |
Promise<boolean> |
Upgrades to "persistent" mode if the persistent-storage permission is already granted; resolves with the resulting mode. |
persisted() |
Promise<boolean> |
true when the bucket is in "persistent" mode. |
estimate() |
Promise<StorageEstimate> |
usage and quota for this bucket alone, unlike navigator.storage.estimate(), which covers the whole origin. |
setExpires(ms) / expires() |
Promise<undefined> / Promise<DOMHighResTimeStamp?> |
Set or read the expiry; expires() resolves null when none is set. |
indexedDB, caches, getDirectory() |
IDBFactory, CacheStorage, Promise<FileSystemDirectoryHandle> |
The bucket-scoped storage endpoints. |
Exceptions
Section titled “Exceptions”open(), keys(), and delete() reject rather than throw, except for the synchronous SecurityError Chromium raises when the whole API is unavailable to the context.
| Exception | Method | Condition |
|---|---|---|
TypeError |
open(), keys(), delete(), estimate() |
No storage shelf is available for the origin (opaque origin, storage disabled); open() also rejects for an invalid name, an expires in the past, or a quota of zero or less. |
InvalidCharacterError |
delete() |
Invalid bucket name, per the specification. Chromium rejects with TypeError here instead (see the observed note). |
InvalidStateError |
persist(), persisted(), estimate(), setExpires(), expires() |
The bucket’s removed flag is set: it was deleted or expired after you obtained the handle. |
QuotaExceededError |
open() |
Chromium-specific: the origin has created more buckets than the implementation allows (Too many buckets created.). |
UnknownError |
open(), keys(), delete() |
Chromium-specific: the storage backend failed (Unknown error occured while creating a bucket., the typo is Chromium’s). |
SecurityError |
any (thrown synchronously) | Chromium denies the API in this context, for example an opaque origin: Access to Storage Buckets API is denied in this context. |
Examples
Section titled “Examples”Both examples resolve a storage endpoint first and run the same application code against it, so the fallback for Firefox and Safari is the origin’s default storage rather than a second code path.
Keeping drafts in a persisted bucket with the default IndexedDB as fallback
Section titled “Keeping drafts in a persisted bucket with the default IndexedDB as fallback”Drafts are the data the user would miss most, so they go in a bucket that asks for persisted: true. When navigator.storageBuckets is missing the same database opens on window.indexedDB, and when the persistence request is refused the code says so instead of assuming the bucket is safe.
function openDatabase(factory, name, version) { return new Promise((resolve, reject) => { const request = factory.open(name, version); request.onupgradeneeded = () => request.result.createObjectStore('drafts', { keyPath: 'id' }); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); });}
async function openDraftsDatabase() { if (!('storageBuckets' in navigator)) { return { db: await openDatabase(indexedDB, 'drafts', 1), persisted: false }; } const bucket = await navigator.storageBuckets.open('drafts', { persisted: true }); return { db: await openDatabase(bucket.indexedDB, 'drafts', 1), persisted: await bucket.persisted(), // false until persistent-storage is granted };}
const { db, persisted } = await openDraftsDatabase();if (!persisted) { document.querySelector('#storage-note').textContent = 'Drafts are stored best-effort and may be cleared under storage pressure.';}The persisted flag comes back false in a fresh Chrome profile because persistent-storage has not been granted; calling navigator.storage.persist() from a user gesture first changes that outcome.
A feed cache that expires on its own
Section titled “A feed cache that expires on its own”A news feed cached for offline reading is worth keeping for a week and worthless after. The bucket carries an expires timestamp so the browser removes it without a cleanup job; the fallback stores the same responses in the default caches and prunes them by a stored date.
const WEEK = 7 * 24 * 60 * 60 * 1000;
async function feedCache() { if ('storageBuckets' in navigator) { const bucket = await navigator.storageBuckets.open('feed', { expires: Date.now() + WEEK }); return bucket.caches.open('articles'); } const cache = await caches.open('articles'); const stamp = await cache.match('/__cached-at'); if (stamp && Date.now() - Number(await stamp.text()) > WEEK) { await caches.delete('articles'); return caches.open('articles'); } if (!stamp) await cache.put('/__cached-at', new Response(String(Date.now()))); return cache;}
const cache = await feedCache();await cache.add('/api/feed?page=1');Opening an existing bucket does not refresh its expiry, so a long-lived feed bucket needs bucket.setExpires(Date.now() + WEEK) after each successful sync to keep sliding the deadline.
See also
Section titled “See also”- IndexedDB: structured client-side storage for PWAs
- Storage persistence, quotas, and eviction, the origin-wide model buckets refine
- Quotas and StorageManager.estimate()
- Cache Storage: the request/response store behind offline PWAs
- Storage Buckets support
- Storage Buckets API: open() method (wicg.github.io)
- Storage Buckets API: validate a bucket name (wicg.github.io)
- Storage Buckets explainer (github.com)
- Bugzilla 1594740: Implement support for storage buckets (bugzilla.mozilla.org)
Specifications
| Specification | Status |
|---|---|
| Storage Buckets | WICG draft |
| Storage Buckets API: open() method | WICG draft |
| Storage Buckets API: the StorageBucket interface | WICG draft |
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Desktop) | Yes | 122 | low | source | 12 |
| Chrome (Android) | Yes | 122 | low | source | 3 |
| Edge (Desktop) | Yes | 122 | low | source | 4 |
| Firefox (Desktop) | No | — | low | source | 5 |
| Safari (macOS) | No | — | low | source | 6 |
- StorageBucketManager (navigator.storageBuckets) shipped in Chrome 122, per MDN browser-compat-data.
- This entry has no MDN prose page yet; verified against caniuse's per-browser support table for this feature.
- Mirrors Chrome Desktop support per MDN browser-compat-data.
- Mirrors Chromium support per MDN browser-compat-data.
- Not implemented; tracked as an open Firefox bug per MDN browser-compat-data.
- Not implemented, per MDN browser-compat-data.