What is the Offline-First Sync pattern?
The app keeps a local database of the data the user needs. Screens read from it, and the user's changes are written to it and to a queue of pending operations. A sync process sends those operations to the backend when the network allows, pulls changes made elsewhere, and resolves conflicts. The user can keep working on a train, in a basement or on a poor connection.
What problem does the Offline-First Sync pattern solve?
Mobile networks are slow, flaky or missing exactly where people use their phones. An app that calls the backend for every screen shows spinners and errors, and loses the user's work when a request fails. Working against a local copy makes the app fast and reliable, and makes the network a background concern.
How do you implement the Offline-First Sync pattern?
- Store the data the user needs locally, and render screens from the local store.
- Record each user action as an operation with a unique ID in an outbox queue, and apply it locally straight away.
- Send queued operations in order when online, with retries and backoff, and have the backend process each operation ID only once.
- Pull changes since the last sync using a cursor or version, and merge them into the local store.
- Decide conflict rules per kind of data: last write wins, merge field by field, or ask the user. Some actions, such as payments, must wait for the server.
When should you use it?
- Users work in places with poor or no connectivity, such as field workers, travellers or warehouses.
- The app must feel instant and never lose work the user has entered.
When should you avoid it?
- Data must always be current, such as stock prices or seat availability, and stale views would mislead.
- Actions are only valid if confirmed by the server at that moment, such as transfers or bookings.
What do you gain?
- Fast screens that do not wait for the network
- No lost work when the connection drops
- Less load from repeated reads
What does it cost?
- Conflict resolution is hard to get right
- More code on every client
- Local data must be secured and cleared on sign-out
Who usually owns the Offline-First Sync pattern?
Product teams. Each product team applies this in the services, screens and data it owns. The platform may offer templates or examples, but the design decisions belong to the team. See how teams map to layers and patterns.
What are common mistakes with the Offline-First Sync pattern?
- Retrying queued operations without idempotency keys, so a flaky network creates duplicates.
- Last write wins applied to everything, silently throwing away a colleague's change.
- Showing an action as done when it still needs the server to accept it, such as a payment.
Offline-First Sync in practice
Notion
Offline Mode was Notion's most requested feature for years. Its local SQLite cache made pages load faster, but only on a best-effort basis, with no guarantee a page would work without a connection.
Notion turned the cache into a persistent storage layer that tracks which pages are available offline, stores what is needed to render them, and records why each one is offline: toggled on, recently visited or inherited from a parent. A page leaves the offline set only when its last reason disappears. Offline pages moved to a CRDT data model for conflict resolution, and clients catch up on reconnect.
Example decision record
- Context
- Field users lose work whenever a request fails on a poor connection.
- Decision
- Write to a local store and an operation queue first, and sync in the background with idempotent operations and per-type conflict rules.
- Consequences
- The app works without a connection and never loses entered work.
- Conflict handling and local data security become part of the client's job.
Test yourself on Offline-First Sync
Field inspectors fill in forms in basements with no signal. The app sends each form straight to the API and shows an error when the request fails, so inspectors retype work later. What design fits?
2 questions involve this pattern. Practice all of them →
Saved in this browser only. See your progress →