Skip to main content

Local-First Sync and Collaboration

Why this chapter matters

Offline-first keeps an app usable during network failure. Local-first goes further: the user's device becomes a primary place where work can be created, edited, searched, and trusted before the server catches up. Collaborative local-first systems also need conflict policy, merge semantics, presence, history, permissions, and recoverability.

This is not only a data-layer topic. It changes UX copy, storage choices, authorization, incident response, analytics, and support workflows.

Core model

Local-first architecture has five contracts:

ContractQuestion
Local truthWhat can the user do before the server confirms it?
Sync truthHow do changes move between device, server, and peers?
Conflict truthWhat happens when valid changes disagree?
Permission truthWhat happens when access changes while a device is offline?
Recovery truthHow does the user understand and repair broken sync?

Architecture topology

UI state
-> local command model
-> durable local store
-> sync queue
-> conflict/merge engine
-> remote API or sync service
-> acknowledgements and patches
-> user-visible sync state

Do not let components write directly to an ad hoc local cache. The app needs a command model that can be replayed, inspected, retried, rejected, or merged.

Storage decisions

NeedCandidateArchitecture concern
Structured local recordsIndexedDBSchema migrations, quota, transaction boundaries, debugging
Small preferenceslocalStorageSynchronous API, sensitive-data risk, limited durability guarantees
Large binary artifactsCache Storage or Origin Private File SystemQuota, eviction, backup, user data expectations
Multi-tab coordinationWeb Locks, BroadcastChannel, SharedWorkerLeader election, duplicate sync, tab lifecycle
Offline shellService workerCache invalidation, update lifecycle, fallback correctness

Sensitive data should not be stored locally unless the product has a clear threat model, retention policy, and user-facing reason.

Conflict strategies

StrategyBest forRisk
Last write winsLow-value preferencesSilent data loss
Server winsCompliance-sensitive recordsUser confusion after local work disappears
Client winsPersonal draftsOverwriting authoritative data
Field-level mergeProfile-like recordsFalse confidence when fields are semantically linked
Operation-based mergeCollaborative editing and countersMore complex command design
CRDT/OTReal-time collaborationLibrary and data-model commitment
Manual resolutionHigh-value ambiguous conflictsUX and support burden

The conflict policy must be chosen per domain object, not per app.

Permission and identity drift

The hard case is not network failure. The hard case is a valid local action that becomes invalid before sync:

  • user role changed
  • account disabled
  • record deleted remotely
  • tenant membership removed
  • policy changed
  • remote version violates a local assumption

The sync engine must distinguish:

  • retryable failure
  • permanent validation failure
  • authorization failure
  • conflict requiring merge
  • destructive remote change requiring user review

User experience states

Every local-first surface needs visible state language:

StateUX obligation
Saved locallyUser can close the tab/device without losing work locally.
SyncingUser knows the app is trying to reach the server.
SyncedUser knows the change is durable remotely.
Needs attentionUser can repair conflict or rejected change.
OfflineUser knows what still works and what is unavailable.
Out of dateUser knows remote changes may be missing.

Avoid vague success messages when the server has not acknowledged the change.

Production controls

  • durable sync queue with schema version
  • command id and idempotency key
  • monotonic local sequence number
  • remote acknowledgement tracking
  • sync pause/resume controls
  • backoff and retry budget
  • conflict fixture library
  • local database migration tests
  • storage quota monitoring
  • support export for sync diagnostics with sensitive fields redacted

Observability

Track:

  • unsynced command count
  • oldest unsynced command age
  • sync success/failure rate
  • conflict rate by object type
  • manual resolution rate
  • local database migration failure
  • storage quota errors
  • multi-tab duplicate sync attempts
  • authorization rejects during replay
  • support tickets with sync diagnostics attached

Review checklist

  • Domain objects have explicit conflict policy.
  • The local store has migration, quota, and corruption handling.
  • Multi-tab sync cannot duplicate side effects.
  • Offline-created commands have idempotency keys.
  • Permission drift has user-visible recovery.
  • Sensitive local data is minimized and justified.
  • Sync state is visible, specific, and accessible.
  • Observability can identify stuck users before support reports.

Exercises

  1. Choose a collaborative object and define its conflict policy: last-write, field merge, operation merge, CRDT, or manual resolution.
  2. Design a sync queue schema with command id, local sequence, remote acknowledgement, retry count, and failure class.
  3. Write UX copy for saved locally, syncing, synced, conflict, authorization rejected, and storage full states.
  4. Create an incident drill for a release that corrupts the local database migration for 2% of users.

Source lens

Use local distributed-systems material for consistency, replication, conflict, and failure thinking. Use browser platform references for IndexedDB, storage quota, service workers, BroadcastChannel, and Web Locks. Use frontend security sources for local sensitive-data risk.