Designing Offline-First Mobile Apps: Sync Strategies That Actually Work

Your app works perfectly in Accra's airport lounge. It falls apart on the road between Kumasi and Sunyani. That gap — between the connectivity you assume and the connectivity your users actually have — is where most mobile products lose trust in the field.

Offline-first architecture is not a feature you bolt on later. It is a foundational decision that shapes your data model, your backend API, your conflict resolution logic, and your release process. Get it right early, and your app becomes a competitive advantage in any low-bandwidth market. Ignore it, and you ship something that frustrates exactly the users you are trying to serve.

This article maps out the three most practical sync strategies teams use today, with honest trade-offs for each.


Why "Cache Everything" Is Not a Strategy

A common shortcut is to cache API responses locally and call it offline support. This works for read-heavy apps — a news reader, a product catalogue — but it collapses the moment a user needs to write data while disconnected. Queued writes pile up, the app has no idea whether a record was already submitted, and when connectivity returns, you face silent overwrites or noisy error screens.

True offline-first means the local database is the source of truth at all times. The network is a sync mechanism, not a dependency.


Strategy 1: Last-Write-Wins (LWW)

How it works: Every record carries a timestamp. When two versions of the same record meet during sync, the one with the later timestamp survives.

Where it fits: Simple entities with infrequent concurrent edits — user profile settings, app configuration, single-owner records.

Implementation sketch in React Native with WatermelonDB:

// Attach a client-generated timestamp on every mutation
const updateProfile = async (changes) => {
  await database.write(async () => {
    await profile.update((record) => {
      Object.assign(record, changes);
      record.updatedAt = Date.now(); // client clock, synced via NTP
    });
  });
};
// During sync, the server compares updatedAt and keeps the larger value

Trade-offs:

  • Dead simple to reason about and implement
  • Clock skew is a real risk — a device with a wrong system clock can silently clobber legitimate updates
  • Acceptable data loss in low-collision domains; catastrophic in shared documents or financial records

Verdict: Use LWW as a default for user-owned data, but never for records multiple users can edit simultaneously.


Strategy 2: Delta Sync

How it works: Instead of syncing full records, the client and server exchange only the changes since the last successful sync, identified by a cursor (a timestamp or a sequence number stored server-side).

Where it fits: Apps with large datasets where syncing everything on reconnect would be too slow or too expensive — field data collection tools, inventory management, logistics apps.

The flow:

  1. Client connects and sends its last known cursor: GET /sync?since=1718200000
  2. Server returns only records created or modified after that cursor
  3. Client applies deltas to the local store and advances its cursor
  4. Deletes are handled via soft-delete tombstones, never hard deletes

Trade-offs:

  • Dramatically reduces bandwidth — critical in data-constrained environments
  • Cursor management adds backend complexity; a missed cursor update corrupts the sync chain
  • Pagination of deltas is mandatory; a naive implementation that returns unbounded results will time out on a slow connection

Verdict: Delta sync is the workhorse pattern for most production offline apps. Pair it with an idempotent apply function on the client so replayed deltas never cause duplicates.


Strategy 3: CRDTs (Conflict-Free Replicated Data Types)

How it works: CRDTs are data structures mathematically designed to merge without conflicts. Any two replicas can be merged in any order and produce the same result — no server arbitration required.

Where it fits: Collaborative features — shared checklists, multi-user forms, real-time annotation tools. Any scenario where two users legitimately edit the same record at the same time.

Common CRDT types your team will actually use:

  • G-Counter / PN-Counter: Distributed counters (votes, inventory quantities)
  • OR-Set: A set where concurrent add and remove operations resolve predictably
  • LWW-Element-Set: A set variant that applies LWW per element rather than per document
  • Automerge / Yjs: Mature JavaScript libraries that implement CRDT-backed JSON documents

Trade-offs:

  • CRDTs require you to model your data in terms of operations, not states — a conceptual shift that takes time
  • Storage overhead is higher; tombstones and vector clocks accumulate
  • Not every business object maps cleanly to a CRDT primitive; sometimes LWW on a sub-field is the pragmatic call
  • Automerge and Yjs both have React Native bindings, but binary size and JSI compatibility deserve a careful audit before shipping

Verdict: Reach for CRDTs when collaboration is a core product requirement, not as a default. The operational overhead is real, and most apps do not need it everywhere.


Choosing the Right Mix

No production app uses a single strategy exclusively. A sensible layered approach looks like this:

Data typeRecommended strategy
User settings / preferencesLast-write-wins
Master data (products, locations)Delta sync (read-only on client)
User-generated records (forms, orders)Delta sync + LWW per record
Collaborative / shared documentsCRDT (Automerge or Yjs)

Practical Principles That Cut Across All Strategies

  • Idempotency is non-negotiable. Every sync operation must be safe to replay. Networks drop mid-sync constantly.
  • Show sync state in the UI. A subtle indicator — "Synced 3 min ago" or "Waiting for connection" — rebuilds user trust immediately.
  • Test on throttled connections, not just offline mode. A 2G connection with 40% packet loss is more destructive than full offline.
  • Version your sync protocol. When your schema changes, old clients still in the field need a migration path.
  • Soft deletes everywhere. Hard deletes break delta sync and make audit trails impossible.

Why This Matters for Your Project

If you are building a mobile product for markets where connectivity is inconsistent — and that describes most of sub-Saharan Africa, rural Southeast Asia, and plenty of urban areas with congested towers — offline-first is not optional. The sync strategy you choose will determine whether your app earns daily active use or becomes a liability your support team manages. Getting this architecture right early costs far less than refactoring a live product with ten thousand users and a backend that was never designed for distributed writes.