# StorageManager.persist() and persisted()

> How navigator.storage.persist() asks for persistent storage, what Chrome, Firefox, and Safari do with the request, and how to code for a false answer.

`navigator.storage.persist()` asks the browser to mark the origin's storage bucket as
persistent, which exempts Cache Storage, IndexedDB, OPFS, and service worker registrations from
automatic eviction under storage pressure. `navigator.storage.persisted()` reports whether that
mark is set. Neither call changes the quota; it changes only whether the browser may clear the
data without the user asking.

## Syntax

```js
navigator.storage.persist()
navigator.storage.persisted()
```

Both return a `Promise<boolean>`. `persist()` fulfils with `true` when the bucket is persistent
after the call, whether it was just granted or already was, and `false` when the browser declined.
`persisted()` only reads the current mode. `persist()` is exposed on `Window` but not inside
workers; `persisted()` is available in both (BCD `api.StorageManager.persist`: Chrome 55,
Firefox 57, Safari 15.2). Both require a secure context.

## Parameters

None.

## 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:` or `file:` document in some engines) or the user has disabled storage for the site. The promise rejects. |

A refusal is not an exception. When the browser decides the site has not earned persistence,
`persist()` fulfils with `false` and the origin stays best-effort.

## How each browser answers

The Storage Standard leaves the decision to the browser, and the three engines took three
different routes ([Storage quotas and eviction criteria](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria),
developer.mozilla.org).

| Engine | Behaviour on `persist()` | What tips the decision |
|---|---|---|
| Chromium (Chrome, Edge) | No prompt. The request is granted or silently denied, and a denied request can be made again later ([Persistent storage](https://web.dev/articles/persistent-storage), web.dev). | Site engagement score, whether the site is installed or bookmarked, whether the `notifications` permission is granted. |
| Firefox | Shows a permission doorhanger and waits for the user. | The user's click. Firefox remembers a block per site. |
| WebKit (Safari 15.2+) | No prompt; the request is auto-granted or denied from the user's interaction history with the site. | Prior first-party interaction with the origin. |

Because Chromium's grant depends on engagement, a call on first paint of a first visit is the
request most likely to return `false`. Calling after the user installs the app, enables
notifications, or completes a meaningful action raises the odds without changing the code.

There is no API to revoke persistence from script. The user removes it through site settings
(Chrome: `chrome://settings/content/siteDetails?site=<origin>`; Firefox: the permissions
section of Page Info), or by clearing the site's data.

## Examples

The examples separate the request (once, after engagement) from the read (every launch), which
mirrors how the two methods are meant to be used.

### Requesting persistence after an engagement signal

Tie the request to the moment the user does something that marks the site as valuable, and act
on the boolean rather than assuming success. The example below runs after a successful install
or a granted notification permission.

```js
async function requestDurableStorage() {
  if (await navigator.storage.persisted()) return true;
  const granted = await navigator.storage.persist();
  if (!granted) {
    console.info('Storage stays best-effort; queued work will be re-synced if evicted.');
  }
  return granted;
}
```

A `false` result leaves the app functional: data written to IndexedDB or Cache Storage is still
there until storage pressure or, in Safari, seven days of browser use without interaction (see
[Eviction and best-effort storage](/reference/storage/eviction/)). Design the sync layer so a
missing record is re-fetched or re-derived rather than treated as corruption.

### Reporting the current mode in a settings screen

`persisted()` is the cheap read; call it on every launch rather than caching the result, because
the user can revoke persistence between visits and Chromium can grant a previously denied
request once engagement rises.

```js
async function describeStorageMode() {
  const persistent = await navigator.storage.persisted();
  return persistent
    ? 'Offline data is protected from automatic clearing.'
    : 'Offline data may be cleared when the device runs low on space.';
}
```

Pair the sentence with the usage figures from
[`StorageManager.estimate()`](/reference/storage/quota-estimate/) so the screen explains both how
much is stored and how safe it is.

### Detecting support and falling back

`navigator.storage` is missing on insecure origins and in browsers older than Chrome 55,
Firefox 57, and Safari 15.2. Treat a missing method as best-effort storage.

```js
async function ensurePersistence() {
  if (!('storage' in navigator) || typeof navigator.storage.persist !== 'function') {
    return false; // not supported: behave as best-effort storage
  }
  try {
    return await navigator.storage.persist();
  } catch (err) {
    if (err.name === 'TypeError') return false; // opaque origin or storage disabled
    throw err;
  }
}
```

The function returns the same `false` for "unsupported", "declined", and "storage disabled",
which is deliberate: the calling code has one fallback, and it is the same in each case.

:::observed
Firefox (English UI) renders the `persist()` request as a doorhanger anchored to the address
bar reading **Allow example.com to store data in persistent storage?**; the string is
`persistentStorage.allowWithSite2` in
[browser.properties](https://github.com/mozilla-firefox/firefox/blob/main/browser/locales/en-US/chrome/browser/browser.properties)
(github.com). After a block, the address bar shows a struck-through storage icon whose tooltip
reads **You have blocked persistent storage for this website.** (`urlbar-persistent-storage-blocked`
in `browser.ftl`), and later `persist()` calls fulfil with `false` without a new prompt until the
user clears the block.
:::

## See also

- [Storage: persist() method](https://storage.spec.whatwg.org/#dom-storagemanager-persist) (whatwg.org)
- [Persistent storage](https://web.dev/articles/persistent-storage) (web.dev)
- [Storage quotas and eviction criteria](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria) (developer.mozilla.org)
- [Eviction and best-effort storage](/reference/storage/eviction/)
- [StorageManager.estimate()](/reference/storage/quota-estimate/)
- [IndexedDB](/reference/storage/indexeddb/)