Sizing Firestore Offline Persistence Without Blowing Up Mobile Storage
Enable Firestore persistence early, size the cache for your device tier, and treat local data as a best-effort approximation. Learn how to avoid blank screens offline and manage write queues safely.
08 Aug 2025, 10:44 UTC

Your app looks fine on Wi-Fi, then a user opens it on a subway and sees a blank list. Writes appear to succeed, then vanish after reconnect. That is the Firestore offline gap: local persistence exists, but it is not enabled early enough, sized for the device, or treated as a cache rather than a source of truth.
The useful takeaway: offline persistence is a capacity decision, not a toggle. Enable it before any Firestore use, choose a cache size that matches device constraints, and treat local data as a best-effort approximation until the server confirms it.
What persistence actually gives you
Firestore persistence stores recently accessed documents locally so reads can be served without network and writes are queued until reconnect. Android and iOS use SQLite; web uses IndexedDB. Listeners receive a local approximation immediately, then update when the server responds.
Two limits matter. Query results are served from cache only if they were previously fetched; arbitrary offline queries are not supported unless the data is already cached. Metadata tells you whether a snapshot came from local storage. Web exposes fromCache; native SDKs expose an equivalent isFromCache flag. Check it when freshness matters, such as prices or availability.
Enable once, early, and size the cache
Enabling persistence is asynchronous and must happen before Firestore is first used. Calling it after a read, write, or listener is created can be ignored or rejected depending on the SDK. Put the call in your app initialization block and wait for it to settle before rendering Firestore-driven UI.
Cache size defaults vary by platform. Web often defaults to tens of megabytes and can be raised with cacheSizeBytes. The setting applies to the whole database, not individual collections. Unlimited cache reduces evictions but can fill device storage and increase startup cost on low-end devices.
Web initialization pattern, run in your app bootstrap before any Firestore reads, writes, or listeners:
// firebase-app.js, run once at startup
import { initializeApp } from 'firebase/app';
import { initializeFirestore, enableIndexedDbPersistence } from 'firebase/firestore';
async function initFirestore() {
const app = initializeApp({ /* your Firebase config */ });
const db = initializeFirestore(app, {
// cacheSizeBytes is a capacity hint for the whole database
cacheSizeBytes: 50 * 1024 * 1024 // 50 MB; adjust for your app
});
try {
await enableIndexedDbPersistence(db);
} catch (err) {
// handle quota or multi-tab conflict
}
return db;
}
// Wait for initFirestore() before rendering Firestore-driven UI.
export const db = await initFirestore();
Required permissions: normal Firestore read/write access through security rules; no extra Firebase permission enables local persistence. Risks: enabling persistence in shared or incognito browsing can hit storage quotas and raise privacy concerns. On web, multiple tabs must share the same persistence settings or you can get a conflict.
Android and iOS enable disk persistence by default in recent SDKs. If you need to change behavior, set it on FirestoreSettings before obtaining any reference. Verify the exact method name and default limit in the SDK reference for your installed version, because defaults change across releases.
Write queue behavior and stale data
Offline writes are stored in a local queue and sent when connectivity returns. The local listener shows the pending write immediately. After reconnect, the server may reject a write due to security rules or a conflict, and the local view will correct.
Practical checks:
- Enable persistence in initialization before any
getDocor collection reference, then observe promise rejection for quota or multi-tab errors. - Inspect IndexedDB or SQLite storage in browser dev tools or device logs while the app runs offline and performs writes.
- Use local logging or Firebase console usage metrics to observe queued writes draining after reconnecting.
Persistence is not a substitute for server-side security rules. Local cache does not enforce access control.
Trade-off to plan for
Large caches and many queued writes slow startup and increase disk pressure. On low-end mobile devices this can cause jank or OS cleanup. Unlimited cache may fill storage and trigger app crashes. Conversely, a cache that is too small evicts data you expect to be available offline.
Offline data can become stale. Surface cache state to users for critical reads, and avoid assuming a document exists offline unless you previously fetched it.
Actionable checklist: enable persistence before first Firestore use, pick a cacheSizeBytes that fits your typical working set and device tier, test offline reads only for previously fetched queries, log cache and pending-write states, and monitor storage after release. Verify behavior on your target SDK versions before shipping.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.