What does `navigator.storage.persist()` do, and what does a `true` result actually guarantee about the data your origin has stored?
answer
- default mode is a loan
- one boolean comes back
- one browser prompts, another decides quietly
- exemption from eviction only
- quota unchanged, user still wins
basics
~20 snavigator.storage.persist() asks the browser to move the origin from best-effort to persistent storage. A true result means the browser will not evict that data automatically under disk pressure. The user can still clear it, and the origin's quota does not change.
solid answer
~50 sStorage is best-effort by default, meaning the browser may throw it away when the device runs short of space. `navigator.storage.persist()` requests the `persistent-storage` permission and resolves to a boolean: `true` means the origin's data is now exempt from automatic eviction. The two big caveats are what it does not buy you. It does not raise the quota — persistence changes eviction policy, not the ceiling — and it does not defend against the user, who can still clear site data from browser settings or a site-issued `Clear-Site-Data` response. How the decision is made also differs: Firefox shows the user a prompt, while Chromium never prompts and instead decides silently from signals like installation, notification permission or site engagement, so a call can simply return `false` with nothing shown. Use `navigator.storage.persisted()` to read the current state without asking.
code
javascript · 9 linesasync function ensurePersistence() {
if (!navigator.storage?.persisted) return 'unsupported';
if (await navigator.storage.persisted()) return 'already-persistent';
const granted = await navigator.storage.persist();
return granted ? 'granted' : 'best-effort';
}
ensurePersistence().then((mode) => console.log('storage mode:', mode));go deeper
Know that client storage is best-effort by default, that navigator.storage.persist() asks to upgrade it, and that the call resolves to a boolean rather than throwing when refused.
Be ready to explain that persistence exempts an origin from automatic eviction only, that it leaves quota untouched, and that Firefox prompts while Chromium decides from engagement signals with no UI.
Show judgment about when to ask: tie the request to a user action that justifies it, and keep a working fallback because most sessions will run best-effort whatever you request.
Own the durability contract. Decide which data may live only on the client, what the recovery path is when it disappears, and how the product earns the automatic grant instead of depending on it.
## Two storage modes The Storage Standard gives every origin's storage one of two modes. **Best-effort** is the default: the browser is free to delete the data when it needs the space back, and it does so without asking and without notifying the page. **Persistent** means the browser undertakes not to evict that data automatically; it stays until the user or the site removes it. This is the single most important framing for client storage. Writing to IndexedDB is not the same as writing to disk in a native app. By default you have a loan, and persistence is the request to upgrade that loan. ## Requesting it ```js if (navigator.storage?.persist) { const granted = await navigator.storage.persist(); console.log(granted ? 'persistent' : 'best-effort'); } ``` `persist()` returns a promise resolving to a boolean. It is a `StorageManager` method, so it requires a secure context, and it applies to the origin's storage as a whole rather than to one database or one cache. Under the hood it is a request for the `persistent-storage` permission, which means it can also be inspected through the Permissions API: ```js const status = await navigator.permissions.query({ name: 'persistent-storage' }); // status.state is 'granted', 'denied' or 'prompt' ``` To find out where you already stand without requesting anything, call `navigator.storage.persisted()`, which resolves to a boolean describing the current mode. ## Browsers decide very differently This is where candidates usually get caught out, because the API looks like one thing and behaves like two. **Firefox treats it as a user permission.** Calling `persist()` shows a prompt asking whether the site may store data persistently, and the promise resolves according to what the user chooses. Because it is a real prompt, it belongs behind a deliberate user action — offering to make a document available offline, for example — not on page load. **Chromium never prompts.** It decides automatically from engagement-style signals: whether the site is installed as an application, whether it has been granted the notifications permission, whether the user has bookmarked it, and how much site engagement it has accumulated. If none of the signals is present, `persist()` simply resolves `false` and the user sees nothing at all. That has a design consequence: you cannot treat a `false` as "the user said no" and you cannot fix it by asking again. You earn it, or you design for going without it. Safari implements the API too, but WebKit layers its own tracking-protection rules on top of storage lifetime, so persistence should not be assumed to behave identically there. ## What `true` does and does not buy you Granted persistence buys exactly one thing: exemption from automatic eviction when the device is under storage pressure. Everything else stays as it was. - **Quota is unchanged.** Persistence is about eviction policy, not size. A persistent origin gets the same disk-derived ceiling, and writing past it still fails. - **The user still wins.** Clearing browsing data, clearing site data for that origin, or removing the site from browser settings deletes persistent storage like any other. A `Clear-Site-Data: "storage"` response header from your own server does the same. - **It is not backup.** The data lives in one browser profile on one device. A reinstall, a new profile, a second browser or a second machine all start empty. - **It does not survive an uninstall** of the browser or a profile wipe. So the honest summary in an interview is: persistence removes one deletion path — the silent, involuntary one — and leaves every other path intact. ## When to ask, and what to do with the answer Ask when the client genuinely holds something the server cannot regenerate: a document being edited offline, a queue of unsynced changes, a large downloaded corpus the user explicitly chose to keep. Ask at the moment that makes the reason obvious to a user, since one browser will show them a prompt. Do not ask reflexively on startup for data that is merely a cache of server state — you will be denied in Chromium, you will burn a prompt in Firefox, and the data was replaceable anyway. Whatever the answer, keep the fallback path. Code that only works when `persist()` returned `true` is code that breaks for most users, because most users have not triggered the automatic grant. Detect an empty store on startup and re-hydrate; sync unsynced work as soon as a network is available rather than trusting local durability. ## A note on buckets The Storage Standard also describes storage buckets, which let a site split its storage into named units and mark individual buckets persistent, so the important bucket can outlive the disposable one. This is available only in Chromium-based browsers, so treat it as a progressive enhancement rather than a design foundation.
- If persist() resolves false in Chromium, what should the app do next?Nothing retry-shaped. Chromium showed no prompt and no user said no, so calling again changes nothing; the grant follows engagement signals such as installation or notification permission. Treat false as the normal case: keep client data replaceable, re-hydrate from the server when the store is empty, and flush unsynced work to the network promptly rather than relying on local durability.
- How do you check whether an origin already has persistent storage without triggering a prompt?Call navigator.storage.persisted(), which resolves to a boolean describing the current mode without requesting anything. You can also inspect navigator.permissions.query({ name: 'persistent-storage' }) and read state, which returns 'granted', 'denied' or 'prompt'. Both are safe to call on startup; persist() is the only call that may show UI.
- Does persistent storage give the origin a bigger quota?No. Persistence and quota are independent. The quota is still derived from the device's disk and is unchanged by the grant, so a persistent origin can still exhaust its budget and see writes rejected. What changes is that the browser will not silently evict the data when the device is short of space.
- Is persistent storage a substitute for syncing data to a server?No. It removes only the automatic-eviction path. The user can still clear site data, a Clear-Site-Data response can wipe it, and the data exists in exactly one browser profile on one device — so a reinstall, a new profile or a second machine starts empty. Anything the user cannot afford to lose still needs a server round trip.
saying these in an interview costs you the question
- Thinks persist() raises the storage quota
- Claims persistent data survives the user clearing site data
- Expects a permission prompt in every browser
- Retries persist() in a loop after it returns false
- Treats persistent storage as a backup or sync mechanism