Most web apps treat the server as the source of truth. Every action waits for a network round trip, and without a connection nothing works.

Local-first software flips that. The primary copy of the data lives on the user's device. The app reads and writes locally, so it is instant and works offline, and a sync layer merges changes across devices and collaborators in the background.

Why local-first is gaining ground

  • Speed – reads and writes are local, so the interface never waits on the network.
  • Offline by default – planes, trains and bad Wi-Fi stop being edge cases.
  • Ownership – users keep their data even if the service changes.
  • Simpler UI code – no loading states for most interactions.

The hard part: merging changes

If two devices edit the same data while offline, what happens when they reconnect? Last-write-wins silently loses work. Manual conflict dialogs frustrate users.

CRDTs (Conflict-free Replicated Data Types) solve this. They are data structures designed so that replicas can accept changes independently and always converge to the same state when they exchange updates, in any order, without a central coordinator.

CRDTs in plain terms

  • A counter CRDT tracks increments per device, so merges add them up correctly.
  • A map CRDT tracks changes per key, so edits to different fields both survive.
  • A list or text CRDT gives every character a stable identity, so concurrent insertions in a document interleave sensibly instead of overwriting each other.

You rarely implement these yourself. Libraries such as Yjs and Automerge provide them.

typescript
import * as Y from "yjs";

const doc = new Y.Doc();
const nodes = doc.getMap("nodes");
nodes.set("idea-1", { title: "Product idea", x: 120, y: 80 });

// Encode local changes and send them to another replica
const update = Y.encodeStateAsUpdate(doc);
// On the other device: Y.applyUpdate(otherDoc, update)

Architecture of a local-first app

  1. Local storage – IndexedDB or SQLite (via WebAssembly or a native runtime) holds the data.
  2. CRDT document – changes are recorded as mergeable operations.
  3. Sync – a server or peer-to-peer channel relays updates. The server can be simple because it does not resolve conflicts.
  4. Export – a clear way to get data out in an open format builds trust.

Trade-offs to consider

  • Authorisation is harder when clients hold full copies of data. Decide what each device may see.
  • Schema changes must handle old data on devices that have been offline for weeks.
  • Storage growth – CRDT history can grow; use compaction features.
  • Not everything fits – bank transfers and inventory need a central authority.

When it is a great fit

Notes, documents, mind maps, design tools, task lists and personal knowledge tools: apps where users mostly edit their own data and value speed and offline use.

Key takeaways

  • Local-first apps keep the primary data on the device and sync in the background.
  • CRDTs let independent edits merge automatically and consistently.
  • Use a library like Yjs or Automerge rather than building CRDTs yourself.
  • It suits personal and collaborative editing tools, not central ledgers.