Local-First Software: Building Collaborative Apps That Work Offline and Sync Safely
The cloud-first default has served software for two decades, but it carries hidden costs: every interaction waits on a network, every feature depends on a server, and every byte of user data lives on someone else’s machine. Local-first software flips that model. It treats the user’s device as the primary source of truth, keeps data instantly available offline, and treats synchronization as a background process that reconciles changes across devices and collaborators. The result is software that feels instant, survives flaky networks, and gives users stronger privacy and ownership. This article explains what local-first really means, how its architecture works, where CRDTs and sync engines fit, and how to decide whether it is right for your product.
What Local-First Actually Means
Local-first is not the same as offline support. Offline support often means a cache: the app reads from a local store when the network is down, but the server remains authoritative. Local-first goes further. The local copy is authoritative for the user’s own edits, and synchronization is a peer-to-peer or client-server process that merges changes without a central gatekeeper. The term was popularized by a 2019 essay from Martin Kleppmann and colleagues, which described seven ideals: no spinners, multi-device, offline collaboration, long-term preservation, privacy, user control, and seamless collaboration.
To understand the shift, compare three models:
- Client-server: The server owns the data. The client sends requests and waits for responses. Conflicts are prevented with locks, transactions, or last-write-wins at the server. This model is simple to reason about but fragile under poor connectivity and expensive to scale for read-heavy collaboration.
- Offline-first: The client stores data locally and queues writes when offline. When connectivity returns, it replays those writes to the server. This improves UX, but the server is still the source of truth, and conflict handling is often bolted on.
- Local-first: Every device has a full or partial replica. Edits are applied locally immediately, then replicated. The system is designed for concurrent changes from the start, so merges are deterministic and automatic. A server may exist as a relay, backup, or discovery service, but it is not required for the core editing experience.
That shift changes the data model. You can no longer assume a single writer, a single clock, or a single authority. You need a way to represent changes, order them causally, and merge them without losing user intent.
The Core Architectural Pieces
1. Local Database and Reactive UI
A local-first app needs a fast, durable store on the device. Common choices include SQLite, IndexedDB, Realm, and embedded key-value stores. The store should support transactions, indexes, and ideally reactive queries so the UI updates automatically when data changes. Optimistic updates are the default: when a user edits a document, the change is written locally and rendered immediately. The sync engine then propagates it in the background.
For web apps, IndexedDB and WebAssembly builds of SQLite are popular. For mobile, SQLite and Realm are common. For desktop, SQLite is often paired with a sync engine. The key requirement is that the local store can represent not just current state but also the metadata needed for synchronization, such as version vectors, operation logs, and tombstones for deleted records.
2. Replication and Sync
Replication is the process of exchanging changes between replicas. A sync engine usually maintains a log of operations or changesets. Each change has an identifier, a causal context, and a payload. Replicas exchange their known state, often using version vectors or state digests, and then request missing operations. The sync server can be a dumb relay that stores and forwards encrypted blobs, or it can be an active participant that validates and merges changes.
Transport can be WebSocket for real-time updates, HTTP for periodic sync, or WebRTC and libp2p for peer-to-peer connections. A robust sync engine handles intermittent connectivity, partial sync, and incremental updates. It should not require sending the entire dataset on every connection.
3. Conflict Resolution
Conflicts are not exceptions in local-first; they are expected. Two devices may edit the same record while offline, or two users may edit the same paragraph at the same time. The system needs a deterministic merge strategy. Common approaches include:
- Last-write-wins: Simple but lossy. It works for preferences or status fields, not for collaborative text.
- Version vectors: Track causality and detect concurrent edits. Useful for custom merge logic.
- Operational transformation: Transforms operations against concurrent operations. Powerful for text editing but complex to implement correctly.
- CRDTs: Conflict-free replicated data types. Data structures designed to merge automatically and deterministically. They are the most common foundation for local-first collaboration.
The choice depends on the data. A counter can use a CRDT counter. A set can use an observed-remove set. A text document can use a sequence CRDT such as RGA or Logoot. A map can use a last-write-wins map with causal metadata. The important part is that the merge rules are explicit and tested.
4. Identity, Auth, and Access Control
Local-first does not eliminate identity. It changes where identity is enforced. Devices need keys, users need accounts, and shared documents need access control. End-to-end encryption complicates server-side authorization because the server cannot read the data. Instead, access control is often enforced through cryptographic capabilities: a user is granted a key or a token that allows them to decrypt and sign changes. Group sharing requires key rotation and re-encryption when membership changes.
Authentication can still use traditional methods such as OAuth or passkeys, but the server should not be able to impersonate the user or read their content. This separation is powerful for privacy, but it adds complexity to sharing, revoking, and recovery.
5. Storage and Schema Evolution
Local-first apps live on devices that update at different times. Schema migrations must be backward and forward compatible. A new version of the app may write data that an old version cannot understand. CRDT libraries often provide their own schema evolution mechanisms, but application-level data still needs versioning. Plan for graceful degradation: old clients should ignore unknown fields rather than crash.
CRDTs in Practice
Conflict-free replicated data types are the engine behind many local-first systems. A CRDT is a data structure that can be replicated across multiple computers, updated independently, and merged without conflicts. The merge is commutative, associative, and idempotent, so the order of updates does not matter. Eventually, all replicas converge to the same state.
There are two broad families. State-based CRDTs send the entire state or a delta and merge states. Operation-based CRDTs send operations and require reliable causal delivery. In practice, many libraries use a hybrid approach with compact deltas and causal metadata.
Common CRDT types include:
- LWW-Register: A single value with a timestamp. The latest write wins. Simple but loses concurrent edits.
- G-Counter and PN-Counter: Counters that support increment and decrement without conflicts.
- OR-Set: A set that supports add and remove operations while preserving concurrent additions.
- RGA, Logoot, and YATA: Sequence CRDTs for collaborative text and lists. They assign unique identifiers to each character or item and merge insertions deterministically.
- Maps and JSON-like structures: Composite CRDTs that nest other CRDTs, often used for documents and app state.
CRDTs are not magic. They carry metadata overhead. A collaborative text document may store identifiers and tombstones for deleted characters. Memory usage can grow, especially in long-lived documents with many edits. Some systems use garbage collection or snapshotting to control size, but that adds complexity. Performance also varies: a CRDT optimized for real-time text editing may not be ideal for large binary files or high-frequency sensor data.
Use CRDTs when concurrent editing is a core requirement and eventual consistency is acceptable. Avoid them when you need strict global ordering, strong transactional guarantees across many records, or a central authority for regulatory reasons.
Sync Engine Patterns
There is no single local-first architecture. The right pattern depends on your product, team, and threat model.
- Client-server relay: The server stores encrypted operations and forwards them to other devices. It does not need to understand the data. This is simple and privacy-friendly, but the server cannot help with indexing or conflict resolution.
- Active sync server: The server understands the data model, validates operations, and may merge changes. This enables server-side search, notifications, and analytics, but it weakens end-to-end encryption.
- Peer-to-peer sync: Devices connect directly using WebRTC or libp2p. This reduces server costs and improves privacy, but NAT traversal, discovery, and offline peer availability are hard problems.
- Hybrid sync: Use a relay for encrypted backup and a server for optional services like search or push notifications. The core editing experience remains local-first.
- End-to-end encrypted sync: Encrypt operations on the client before sending. The server sees only ciphertext. This requires client-side key management, search, and conflict resolution. It also leaks metadata such as document size and sync frequency unless you add padding and cover traffic.
Many teams start with a managed sync service or an open-source engine such as Automerge, Yjs, Electric SQL, Replicache, or Dexie Cloud. Building a sync engine from scratch is a multi-year effort if you need robust offline support, encryption, and schema evolution. Reusing a proven engine lets you focus on product differentiation.
Designing the Data Model for Local-First
The data model is where local-first projects succeed or fail. A model designed for a central database often assumes auto-incrementing IDs, server timestamps, and exclusive writes. Those assumptions break in a distributed system.
Follow these principles:
- Use stable, globally unique IDs: UUIDs, ULIDs, or content-addressed identifiers. Never rely on auto-incrementing integers from a central server.
- Model operations, not just state: An operation log lets you replay, audit, and merge changes. Event sourcing is a natural fit, though it adds storage and complexity.
- Use logical clocks: Lamport timestamps or hybrid logical clocks provide causal ordering without relying on synchronized wall clocks. Wall clocks can skew, jump, and be manipulated.
- Design for soft deletes: Deletion in a distributed system is often a tombstone. A hard delete on one device may be resurrected by a concurrent edit on another. Tombstones need garbage collection policies.
- Version your schema: Include schema versions in records. Migrate locally and handle unknown fields gracefully.
- Keep documents small enough to sync: Large monolithic documents increase merge cost and sync latency. Split data into independently syncable units when possible.
A good local-first data model also supports time travel and undo. Because operations are preserved, you can build features that let users inspect history, restore previous versions, or branch a document. These features are difficult in a traditional server-authoritative model.
Security and Privacy Model
Local-first can improve privacy, but it is not automatically private. The threat model matters. If the server stores encrypted blobs, it cannot read user data, but it can still observe metadata. If a device is compromised, the attacker may access local data and keys. If sharing is implemented poorly, revocation may not be immediate.
Key considerations include:
- End-to-end encryption: Encrypt data before it leaves the device. Use established protocols and libraries rather than inventing cryptography.
- Key management: Devices need keys. Recovery, rotation, and multi-device onboarding are hard. Consider passkeys, hardware-backed keystores, and social recovery.
- Access control: Capability-based sharing can grant read or write access without a central server. Revocation requires key rotation and re-encryption for forward secrecy.
- Metadata leakage: The server may see who syncs with whom, when, and how much. Pad messages or use private information retrieval if metadata matters.
- Compliance: Data minimization and user control align well with GDPR and similar regulations. But you still need data export, deletion, and audit trails where required.
For many products, a hybrid model works best: end-to-end encryption for user content, plus optional server-side services that users can enable. Make the trade-offs visible and give users control.
Operational Concerns
Local-first shifts complexity from the data center to the edge. That does not eliminate operations; it changes them. You still need sync servers, but they may be simpler relays. You still need monitoring, but now you must observe per-device sync health.
Key operational metrics include:
- Sync lag: Time between a local edit and its propagation to other devices.
- Conflict rate: Frequency of concurrent edits and merge outcomes.
- Operation backlog: Number of unsynced operations on a device.
- Storage growth: Size of local databases and operation logs over time.
- Error rates: Failed sync attempts, decryption errors, and schema migration failures.
Testing is equally important. Unit tests are not enough. You need property-based tests for merge functions, fuzz tests for sync protocols, and integration tests that simulate network partitions, clock skew, and long offline periods. Build tools to inspect operation logs, visualize causality, and replay sync sessions. When a user reports a sync issue, you need to understand the sequence of operations across devices.
Deployment also matters. Mobile app stores may delay updates, so old clients persist. Version skew is normal. Your sync protocol must support backward compatibility or force upgrades gracefully. Rollbacks should not corrupt data. Migrations should be reversible or at least safe to retry.
Implementation Checklist
If you are considering local-first, use this checklist to scope the work:
- Define consistency requirements per feature. Not every feature needs full CRDT collaboration. Some can be last-write-wins or server-authoritative.
- Choose build versus buy. Evaluate Automerge, Yjs, Electric SQL, Replicache, Dexie Cloud, and managed sync services. Compare encryption, offline support, conflict resolution, and platform support.
- Model data as operations with stable IDs and causal metadata. Avoid auto-incrementing IDs and server timestamps.
- Encrypt before sync if privacy is a goal. Plan key management, recovery, and sharing from the start.
- Build local-first developer tools. Operation log inspectors, time travel, and sync simulators save months of debugging.
- Test offline, reconnection, concurrent edits, and long-lived branches. Use property-based testing for merge logic.
- Plan for backups and data export. Local-first does not mean no backups. Users can lose devices, and sync servers can fail.
- Decide what runs on the server. Search, notifications, analytics, and billing may still need a server. Keep the core editing experience local-first.
When Local-First Is the Wrong Choice
Local-first is powerful, but it is not a universal architecture. It may be the wrong choice when:
- A central authority is required: Payments, inventory allocation, and regulated audit trails often need a single source of truth and strict serializability.
- Data is too large for devices: If the dataset is terabytes and users only need a small slice, a server-side query model is more practical.
- Strong real-time global ordering is essential: Some systems need a total order of events across all participants. CRDTs provide eventual consistency, not global consensus.
- The team lacks distributed systems expertise: Building a correct sync engine is hard. If local-first is not core to the product, a managed service or a simpler offline cache may be enough.
- Regulatory requirements mandate server-side inspection: End-to-end encryption can conflict with content moderation or lawful access requirements. Understand the legal landscape before committing.
In many cases, a hybrid approach is best. Use local-first for the user-facing editing experience, and use server-side services for features that genuinely need central coordination.
The Road Ahead
Local-first software is moving from research prototypes to production systems. Standardization efforts are emerging around CRDT formats, sync protocols, and encryption. Edge computing is converging with local-first: devices are powerful enough to run databases, ML models, and sync engines. AI agents can operate on local data without sending everything to the cloud, which improves latency and privacy.
The cloud will not disappear. It will become a relay, a backup, and an optional service layer. The most compelling products will be those that give users the instant, resilient, and private experience of local software while still enabling collaboration and multi-device access. Local-first is not just an architecture. It is a commitment to user ownership and to software that works when the network does not.

