Mobile Development17 March 2026·12 min read

Building Offline-First Mobile Apps with React Native

Design mobile apps that work without connectivity. AsyncStorage, WatermelonDB, sync strategies, conflict resolution, and offline queue patterns.

React NativeOffline-FirstWatermelonDBAsyncStorageSyncMobile Apps

Why Offline-First Matters

In markets like India, unreliable connectivity is not an edge case — it is the default. Users commute through dead zones, work in areas with intermittent 4G, and deal with network drops dozens of times daily. An app that shows a spinner or an error screen when the network drops is an app that gets uninstalled.

Offline-first means designing your app to work without a network connection by default, and treating connectivity as an enhancement rather than a requirement.

The Offline-First Architecture

An offline-first React Native app has three key layers:

Local database: All data is stored on-device and read from the local store. The UI never directly fetches from the network.
Sync engine: A background process that synchronizes local data with the server when connectivity is available.
Conflict resolution: A strategy for handling situations where the same data was modified both locally and on the server.

Local Storage Options in React Native

AsyncStorage

AsyncStorage is the simplest option — a key-value store similar to localStorage on the web. It works well for:

User preferences and settings
Authentication tokens
Small amounts of cached data (under 1000 items)

Limitations: AsyncStorage serializes everything as JSON strings. Querying is limited to key lookups — you cannot filter, sort, or join data efficiently. For any meaningful data set, you need a proper database.

WatermelonDB

WatermelonDB is purpose-built for offline-first React Native apps. It uses SQLite under the hood but provides a reactive, lazy-loading API optimized for large datasets.

Key advantages:

Lazy loading: Components only load the records they need. A list of 100,000 records renders instantly because WatermelonDB only fetches visible items.
Reactive updates: When data changes, subscribed components re-render automatically. No manual cache invalidation.
Sync primitives: Built-in pull/push sync protocol that handles most sync scenarios out of the box.
Schema migrations: Versioned schema changes that run automatically on app update.

SQLite with expo-sqlite

For teams that want full SQL control, expo-sqlite provides direct SQLite access. You write raw SQL queries and manage your own ORM layer. This gives maximum flexibility but requires more boilerplate.

Sync Strategies

Pull-Push Sync

The most common pattern for offline-first apps:

Pull: Fetch changes from the server since the last sync timestamp. Apply them to the local database.
Push: Send local changes (creates, updates, deletes) to the server. Mark them as synced on success.

WatermelonDB implements this pattern natively. You provide a pullChanges function that fetches server changes and a pushChanges function that sends local changes. The library handles the rest.

Event Sourcing

Instead of syncing current state, sync events (actions). Each user action is recorded as an immutable event:

"User created task: Buy groceries"
"User marked task complete: Buy groceries"
"User deleted task: Buy groceries"

The server replays events to compute current state. This approach provides a complete audit trail and simplifies conflict resolution but increases storage requirements.

Conflict Resolution

Conflicts occur when two devices modify the same record while offline. There are several strategies:

Last Write Wins (LWW)

The most recent change (by timestamp) overwrites earlier changes. Simple to implement but can lose data if two users edit different fields of the same record.

Field-Level Merging

Instead of replacing the entire record, merge at the field level. If User A changed the title and User B changed the description, both changes are preserved. This requires tracking which fields were modified in each change.

Operational Transform / CRDTs

For collaborative editing scenarios, CRDTs (Conflict-free Replicated Data Types) allow concurrent edits to be merged automatically without conflicts. Libraries like Yjs and Automerge implement CRDTs for JavaScript. This is the most complex approach but eliminates conflicts entirely.

Offline Queue Pattern

For actions that require server processing (payments, API calls to external services), implement an offline queue:

Queue the action locally: Store the action with its parameters in a persistent queue.
Process when online: When connectivity returns, process queued actions in order.
Handle failures: If an action fails, retry with exponential backoff. After maximum retries, notify the user.
Idempotency: Ensure server endpoints are idempotent — processing the same action twice should produce the same result. Use client-generated UUIDs as idempotency keys.

Network Detection

React Native provides the @react-native-community/netinfo library for network state detection:

Connection type: WiFi, cellular, ethernet, or none
Is connected: Boolean indicating network availability
Is internet reachable: Actual internet connectivity (not just local network)

Use isInternetReachable rather than isConnected — a device can be connected to WiFi without internet access (captive portals, restricted networks).

Optimistic UI Updates

Show changes immediately in the UI, before the server confirms them. This makes the app feel instant regardless of network speed:

User taps "Complete Task" → UI immediately shows the task as complete
Background sync sends the change to the server
If sync fails, revert the UI change and notify the user

This pattern is essential for offline-first apps. Users should never wait for a network response to see the result of their action.

Testing Offline Scenarios

Testing offline behavior requires simulating various network conditions:

Airplane mode: Complete network loss
Slow 2G/3G: Use network link conditioner (iOS) or Android emulator network throttling
Intermittent connectivity: Toggle airplane mode rapidly during sync operations
Server downtime: Point the app at a non-responsive server

Build automated tests that simulate these conditions. The most critical test: start offline, create data, go online, verify sync, go offline again, modify the same data on the server, go online, verify conflict resolution.

Offline-first is harder to build but dramatically improves user experience in real-world conditions. Need help building offline-capable mobile apps? Contact us.

BH

The Beyond Horizon Team

We are a digital agency based in Ajmer, India, specializing in Next.js web applications, React Native mobile apps, and UI/UX design. 150+ projects delivered.

About Us →

Have a project in mind?

We build fast, SEO-ready web and mobile applications.

Get a Free Consultation