Building Offline-First Apps: Why It Matters and How to Do It
The technical and product case for designing apps that work without connectivity.
What Is Offline-First?
An offline-first app is designed to work fully without an internet connection, and syncs with a server when connectivity is available — not the other way around.
Most apps are online-first: they make a network request, wait for a response, and then render. When there's no connection, they break. They show error messages. They freeze. They lose data.
Offline-first apps do the opposite. All reads and writes happen against a local data store first. Network requests happen in the background when possible. The user never waits for the network.
Why It Matters More Than Ever
Connectivity is still unreliable for most of the world. Even in the UK, users regularly experience poor signal on public transport, in basements, at events, and in rural areas.
An app that works offline works everywhere. And "everywhere" is a significant competitive advantage.
Beyond connectivity, offline-first apps are faster. Reading from a local database takes microseconds. Reading from a remote API takes hundreds of milliseconds at best. The performance difference is immediately noticeable to users.
For certain categories — education, health, productivity, utilities — offline capability is not a nice-to-have. It is a core product requirement.
The Technical Approach
Local Storage First
Every data operation (read and write) should go through a local data layer first:
- Flutter: use Hive, Drift (SQLite wrapper), or Isar for the local database.
- React Native: use WatermelonDB or SQLite.
- Web: use IndexedDB via Dexie.js.
Write to local storage immediately. Queue the network sync separately. The user sees their action reflected instantly — no spinner, no waiting.
Sync Architecture
The sync layer handles getting local changes to the server and remote changes to the device.
Simple approach: timestamp-based sync. Every record has an updatedAt timestamp. On sync, send all records updated since last sync. Receive all server records updated since last sync. Apply changes.
Conflict resolution strategy: for most apps, last-write-wins is sufficient. For collaborative apps, you need a more sophisticated approach (CRDTs or operational transforms).
Connectivity Detection
- Flutter: use the
connectivity_pluspackage. - React Native: use
NetInfo. - Web: use the
Navigator.onLineAPI with event listeners for online/offline events.
Show a subtle offline indicator (a small banner or icon) so users know the sync status. Don't block the UI — just inform.
Common Mistakes
Treating sync as an afterthought. Offline-first architecture needs to be designed upfront. Retrofitting sync onto an online-first app is painful and often requires rewriting core data layers.
Not handling conflicts. If two devices edit the same record while offline, you have a conflict. Decide your resolution strategy before you build, not after your first user loses data.
Syncing too much. Not everything needs to sync. User preferences, local settings, cached content — these can often remain device-only. Sync only what genuinely needs to be shared across devices.
Our Experience
Several of our apps — including Lakshya (exam prep) and One Healthy Habit — are built offline-first. Users in areas with poor connectivity can access their full question banks, complete sessions, and have everything sync when they're back online.
The feedback is consistent: "It just works." That's the goal.
Want to build something?
If this guide has sparked an idea, we'd love to hear it.