How to build an offline first app that keeps working when the signal drops

A practical guide to building an offline first app the Zarle way: local database as source of truth, background sync, conflict resolution, and Firestore offline persistence.

ZZarle Infotech
August 25, 2026 13 min read
how to build an offline app - Young man smiling while looking at the phone screen

Most people think building an offline first app means adding a cache. Store a few API responses, show them when the network is gone, done. Then the user goes into a lift, opens the app, tries to save something, and the whole thing freezes on a spinner. The cache was there. It just was not the point.

An offline first app is a different design decision, made before you write the first screen. It means the app never waits for the network to do its job. You read from local storage, you write to local storage, and a sync engine deals with the server quietly in the background whenever a connection shows up. Get that right and the app feels instant even on a train through a dead zone. Get it wrong and you have a cache pretending to be an offline first app, which is worse than nothing because it lies to people.

We are Zarle Infotech, a Noida studio, and we build mobile apps in-house. This guide is how we actually approach the problem: what offline really means, why caching is not it, how conflict resolution decides everything, how Firestore hands you most of this for free on React Native, and why in India this is a requirement rather than a feature. We verified the current tooling against 2026 sources before writing, but the opinions here come from our own builds.

Caching is not the same as an offline first app

Here is the distinction we make on day one of any project. Caching stores the answers to questions you already asked. Building offline first lets people keep asking new questions with no signal at all.

Say someone opens a learning app on a metro. A cached app can show them the lesson they already downloaded. The offline-first version lets them finish the lesson, mark it complete, take the quiz, and save their score, all while the phone has zero bars, and then it pushes every one of those changes to the server the moment they surface at the next station. The user never sees a difference between online and offline. That is the whole game.

The mechanism behind it has three parts, and you need all three:

  • A local database that is the source of truth. The app reads and writes to local storage first, instantly. The server is a copy that eventually agrees, not the thing you wait on.
  • A background sync engine. It pushes local changes up and pulls remote changes down whenever a connection is available, and it survives the app being closed.
  • Conflict resolution. When the same record changed in two places, something has to decide who wins. This is the hard part, and it is where most teams get caught out.

Caching gives you the first sliver of the first part and none of the rest. That is why we never call a cached app offline-ready.

CachingOffline first app
Source of truthThe server, cache is a copyThe local database on the device
Writes while offlineUsually blocked or lostSaved locally, queued for sync
What the user can do offlineRe-read what loaded beforeKeep using the app fully, read and write
SyncManual refresh, often noneAutomatic background sync on reconnect
Conflict handlingNot designed for itDesigned up front, per data type
Feels likeWorks until you lose signalWorks the same online and offline

Design conflict resolution before you build, not after

If you take one thing from this guide, take this: you decide how conflicts resolve up front, per data type, before a line of sync code is written. Bolting it on after launch is where these projects go to die.

A conflict happens when the same piece of data changed on two devices, or on a device and the server, while they were out of touch. Someone edits their profile on their phone offline while a support agent edits it on the web. Both come back online. Who wins? If you have not decided, the sync engine will decide for you, usually badly, and someone's work vanishes.

There are two approaches we reach for, and we pick per data type, not per app:

  • Last write wins, using timestamp metadata. Every write carries the time it was made. When two versions meet, the newer timestamp wins and the older one is discarded. It is simple, cheap, and correct enough for most business apps, a profile edit, a settings toggle, an order status. The catch, and current 2026 write-ups on conflict resolution agree here, is that last write wins is quietly destructive when two people genuinely edited at the same time, because one person's change is silently thrown away.
  • CRDTs, conflict-free replicated data types. These let multiple people edit shared data offline and concurrently, and the changes merge without losing anyone's work when they reconnect. This is what you want for collaborative documents, shared lists, or field teams who work disconnected for hours. The cost is real engineering complexity, so you only pay for it where losing data is unacceptable.

The rule we follow: last write wins for almost everything, CRDTs only for the specific data where two people editing at once is a normal event and every edit must survive. Deciding this per data type, before the build, is the difference between a sync engine you trust and one you are constantly firefighting.

Firestore offline persistence does most of the work on React Native

Here is the good news that shortens a lot of these projects. You often do not have to build the sync engine yourself.

Our default mobile stack is React Native, and a big reason is that our web is built in React and Next.js with TypeScript, so we reuse the same functions, references, and mental model across web and app. That same stack pairs naturally with Firebase, and Firestore's offline persistence is, out of the box, exactly the pattern described above. On iOS and Android it is enabled by default. When the app reads data, Firestore caches it locally. When the app writes, it updates the local cache immediately and queues the write. If the network is down, the queue persists, even if the user closes the app, and the moment connectivity returns the SDK syncs automatically. Reads, writes, and queries all keep working with no connection.

That is the reason we reach for Firebase so often on mobile. The offline behaviour is genuinely mature and it just works, which is not something you can say about every sync layer. If you are weighing this against rolling your own, our full take is in our guide on Firebase vs a custom backend. The short version we tell clients: infra is cheap, people are expensive, so start on a managed backend and let it handle sync unless you have a reason not to.

One practical gotcha from the field, confirmed by 2026 React Native write-ups: do not wrap Firestore writes in await when you are relying on offline mode. If you await a write that has already landed in the local cache, the UI can lock waiting for a server round trip that will not come until the network is back. Use the local callback, let the write settle locally, and let sync happen in the background. That one habit is the difference between an offline first app that feels instant and one that stutters every time the bars drop.

When to build your own sync instead

Firestore is our default, not our religion. You go custom, usually SQLite or WatermelonDB with your own sync layer, when your sync rules are too specific for a managed backend to express cleanly. Complex per-field merge logic, unusual ordering guarantees, strict data-residency rules, or a domain where you need CRDTs the BaaS does not offer. That is an earned decision, made when you hit a wall, not a starting point. Building a bespoke sync engine for an app that does not have users yet is exactly the over-engineering we tell founders to avoid.

If you are still choosing your framework before you get to any of this, our cross-platform app development guide and our Flutter guide walk through the trade-offs. Flutter has solid offline patterns too, but our React Native plus Firestore combination is where we ship the fastest.

Why an offline first app is not optional in India

In a lot of markets, offline is an edge case. Here it is the normal case, and it changes how we build.

Connectivity in India is patchy in places people use their phones most: on the metro, on trains between cities, in basements and lifts, and across tier-2 and tier-3 towns where a strong signal in the morning is a weak one by evening. On top of that, the entry-tier device is still a 4 GB RAM, 64 GB storage phone that is already juggling Chrome, WhatsApp, and YouTube. An app that freezes the second the signal drops does not read as "network issue" to that user. It reads as "this app is broken," and a broken app is the first thing uninstalled to free up space.

So for anything people use on the move, and that is most things, delivery and field work, learning, commerce, health check-ins, offline-first support is the line between "reliable" and "gone." We have felt this across the range of apps we have shipped, from a learning platform like Optima Learning where students study on commutes, to consumer apps like Chefadora and Marcoob where a frozen screen at a bad moment costs a session you do not get back. Building for the good-signal demo and ignoring the bad-signal reality is how good apps quietly fail.

How we build an offline first app, step by step

The process is not exotic, but the order matters. Skip a step and you pay for it in a rewrite.

  1. Map the data and pick a conflict strategy per type. Before design, we list every kind of data the app touches and mark each one last write wins or CRDT. This is the decision that shapes everything else.
  2. Make the local database the source of truth. The UI reads only from local state. Nothing on screen waits for the network. On our React Native builds this is usually Firestore's local cache doing the work.
  3. Wire the background sync. Local writes queue and push on reconnect, remote changes pull down. With Firestore this is mostly configuration. With a custom stack it is the bulk of the engineering.
  4. Handle the offline states in the UI. Show a quiet "saved, will sync" state rather than an error. People trust an app that admits it is offline and promises to catch up.
  5. Test on real devices and real bad networks. We test on the cheap Android phones our clients' customers actually use, with the network toggled off mid-action, on patchy 4G, and in airplane mode. A sync that works on office wifi and fails on a moving train is a broken sync.

That fifth step gets skipped constantly and it is the one that catches the real bugs. Offline behaviour only shows its problems under real conditions, so we force those conditions before launch, not after.

A quick checklist before you build

  • Have you decided, per data type, whether conflicts resolve by last write wins or CRDT?
  • Is the local database the source of truth, with the UI never waiting on the network?
  • Does the sync survive the app being closed and resume on its own?
  • Are you using Firestore's built-in offline persistence, or do you genuinely need a custom sync layer?
  • Have you tested with the network switched off mid-action on a real budget phone?
  • Does the UI clearly tell people their work is saved and will sync?

If you can answer those, you are ahead of most teams that come to us after their "offline" app froze in front of a client.

how to build an offline app - Lovely pretty girl with collected wavy hair wearing sunglasses blue shirt and striped tshirt sitting on the bench in city garden in sunlight and scrolling smartphone with smile
how to build an offline app - Lovely pretty girl with collected wavy hair wearing sunglasses blue shirt and striped tshirt sitting on the bench in city garden in sunlight and scrolling smartphone with smile

Frequently asked questions

What is the difference between caching and an offline first app?

Caching stores responses you already fetched so you can re-read them without a network. An offline first app makes the local database the source of truth, so people can read and write and use the whole app with no signal, and a background sync engine reconciles with the server later. Caching is a small part of the first idea; going offline first is the full pattern with sync and conflict resolution built in.

How do you handle conflicts in an offline first app?

You decide up front, per data type. Most business data uses last write wins, where each change carries a timestamp and the newer one wins. Shared or collaborative data, where two people editing at once is normal and no edit can be lost, uses CRDTs, which merge concurrent changes automatically. The mistake is leaving this until after launch.

Does Firestore make an app offline first automatically?

Close. Firestore's offline persistence is enabled by default on iOS and Android. It caches reads, applies writes to the local cache immediately, queues them while offline, and syncs automatically on reconnect. That covers the core of offline support for most projects. You still design your conflict strategy and your offline UI states yourself.

When should I build a custom sync engine instead of using Firebase?

Only when your sync rules are too specific for a managed backend, such as complex per-field merges, strict data-residency requirements, or CRDT needs the BaaS cannot express. Then a stack like SQLite or WatermelonDB with your own sync makes sense. For most apps, Firestore's built-in sync is faster to ship and cheaper to maintain.

Why does offline matter so much for apps in India?

Because connectivity is patchy where people actually use their phones, on metros, trains, and across tier-2 and tier-3 areas, and entry-tier phones are already stretched. An app that freezes when the signal drops gets read as broken and uninstalled. For anything used on the move, an offline first app is the difference between reliable and gone.

Can I add offline support to an existing app later?

You can, but it is harder than building it in from the start, because making the local database the source of truth touches how every screen reads and writes data. If offline matters to your users, design for it up front. Retrofitting usually means reworking the data layer, which costs more than doing it once, correctly.

Where to start

If your users spend time on the move or on weak networks, an offline first app is not a luxury feature to add in version two. It is a design decision that belongs in the first conversation, because it shapes your data model, your backend choice, and your testing. We build mobile apps in-house on React Native and Firebase, we design conflict handling before we write sync code, and we test on the phones and networks your customers really have.

Have a look at how we work on our mobile app development page, and if it fits, tell us what you are trying to build and where your users lose signal.

Related articles

More on Mobile Apps

More on Web Development