Skip to content
← All writing
February 18, 2026·4 min read

Local-First Apps: Building for Offline

Most apps are built cloud-first: the server holds the real data, and the app is a window onto it. Lose connectivity and the app stalls. Local-first flips that model — the device holds the data, the app works instantly whether or not there's a network, and syncing to the cloud happens quietly in the background. This post explains the approach, its benefits, and the genuinely hard part.

The core idea

In a local-first app, the local device is the primary source of truth. Every read and write hits local storage first, so the interface responds immediately. Changes are then synced to a server (and to the user's other devices) whenever a connection is available. The network becomes an enhancement, not a requirement.

Contrast that with the cloud-first default, where a tap waits on a server round trip and a dropped connection means a spinner or an error. Local-first inverts the dependency.

Why it's worth the effort

  • Instant interactions. Reading and writing local data has no network latency, so the app feels immediate — every time.
  • True offline support. Subways, planes, dead zones, flaky connections — the app just works, and syncs later.
  • Resilience. A backend hiccup doesn't take the user's experience down with it.
  • Often better privacy. Data can live primarily on the device, syncing only what's needed.

For apps people use frequently or in unreliable-network conditions, this is a real, felt quality difference.

How syncing works

The heart of a local-first app is the sync engine. The usual pattern:

  1. Write locally first. The change is saved to the device and the UI updates immediately.
  2. Queue the change. It's added to a list of pending changes to send.
  3. Sync when possible. When connectivity allows, pending changes are pushed to the server and remote changes are pulled down.
  4. Merge. Local and remote states are reconciled into a consistent result.

Steps 1–3 are mechanical. Step 4 is where the difficulty lives.

The hard part: conflicts

When the same data can be edited on multiple devices while offline, two versions can diverge. Deciding what the "correct" merged result is — without losing anyone's work — is the central challenge of local-first design. Common strategies:

  • Last-write-wins. Simplest: the most recent change overwrites the others. Easy to implement, but it can silently discard edits. Fine for low-stakes data, risky for important data.
  • Field-level merging. Merge changes to different fields of the same record automatically, and only treat edits to the same field as a real conflict. Much friendlier in practice.
  • CRDTs (Conflict-free Replicated Data Types). Specialized data structures designed so concurrent edits always merge to a consistent result without manual conflict resolution. Powerful, but more complex to adopt.
  • Ask the user. For genuinely irreconcilable conflicts, surface them and let the person choose. Rare if your merging is good, but a necessary escape hatch.

Choose based on your data's stakes: a draft note can tolerate last-write-wins; a shared financial ledger cannot.

Practical guidance

  • Give records stable IDs. Generate IDs on the device (not sequential server IDs) so offline-created items don't collide when they sync.
  • Track sync state. Know which local changes are pending, synced, or failed — and be able to retry.
  • Design the data for merging. Smaller, independent fields conflict less than one big blob. Structure matters.
  • Show sync status honestly. A subtle indicator of "saved locally / syncing / synced" builds trust and sets expectations.
  • Consider a framework. Sync is hard to build well; mature local-first/sync libraries can handle much of the heavy lifting.

Is it always worth it?

No. Local-first adds real complexity, and not every app needs it. If your app is inherently online (a live feed, a multiplayer game, anything requiring the freshest server state every second), cloud-first is the honest fit. Local-first pays off most for productivity, notes, tracking, and tools people rely on regardless of connectivity.

Summary

Local-first architecture makes the device the source of truth, so apps feel instant and keep working offline, syncing in the background when they can. The benefits — speed, offline support, resilience — are substantial, but the price is handling sync and, above all, conflicts. Pick a conflict strategy that matches your data's stakes, use device-generated IDs and clear sync state, and lean on a sync library where you can. For apps used often and in the real, unreliable world, it's an approach well worth the effort.