Aller au contenu principal
All articles
AI

Local-first + CRDT: the new 2026 SaaS architecture

IndexedDB, Yjs, server sync, Postgres materialisation: local-first becomes the 2026 SaaS architecture for write-heavy apps.

VALRY LABS Engineering TeamJuly 14, 202612 min read

The local-first thesis: data lives on the device first

The dominant SaaS model of the last fifteen years rests on a single postulate: the server is the source of truth, the client is only a shop window. Every read starts from the browser, crosses the network, hits an API, queries a database, then returns. Every write follows the reverse path, blocking the user until the response. On paper, it is simple. On a train, in a plane, or on a flaky network, it is a catastrophic experience.

The local-first thesis inverts the polarity. Data lives first on the user's device, in a persistent local database (IndexedDB, SQLite WASM, OPFS). The cloud becomes a replication peer, not an oracle. Reads become instant: no network, no latency, no loading state. Writes become instant: you write locally, queue, and sync when the network comes back. The application fully works offline. The experience difference versus an always-online SaaS is visible from the very first second.

The benefits are not only ergonomic. Availability increases (no user-facing downtime), infrastructure cost drops (fewer server reads, less cache churn), resilience improves (a fibre cut no longer stops work). For write-heavy applications — notes, tasks, forms, briefs, field CRM — it is a category change. For read-only analytical dashboards, it is useless. The first lesson: local-first is not a universal dogma, it is a tool for a certain profile of application.

One mathematical obstacle remained: how to synchronise several editors without a central server sequencing the operations? That is where CRDTs come in.

CRDT: the mathematics that make automatic merging possible

A CRDT (Conflict-free Replicated Data Type) is a data structure that converges mathematically, whatever the order in which replica updates arrive. Concretely: two users edit the same paragraph at the same time, offline, then re-sync — the system produces a coherent final state with no human intervention, no last-write-wins, no manual merge. It is the building block that makes local-first workable.

Two libraries dominate the ecosystem in 2026. Yjs, written by Kevin Jahns, has established itself as the production choice for most collaborative editors. Its reference algorithm, YATA, handles concurrent operations on rich text with controlled complexity. Automerge, initiated by Martin Kleppmann (author of "Designing Data-Intensive Applications"), bets on a more generic JSON-like model and on the Mark-On-Doc algorithm for text fusion. For structured documents, Automerge is often more natural; for text editors, Yjs remains more memory-efficient.

The key property, common to both, is deterministic merging: given two states with their history, the fusion result is identical whatever the path taken. That shifts the problem: you no longer ask a central server to arbitrate conflicts; you delegate arbitration to the data structure itself. The server becomes a relay of updates, not a decider. It is what lets Linear open a ticket offline, Notion merge two paragraphs edited simultaneously, and Figma offer branching on a multiplayer design.

The trade-off: CRDTs have a cost. In memory first (the operation history can grow), then in schema complexity (a CRDT map is not a native map, a CRDT array behaves differently from a plain array). It is a model that demands discipline, not a magic library.

The 2025-2026 production wave: local-first leaves the lab

For a long time, CRDTs were an academic topic. The first implementations were slow, hungry, and hard to integrate. The work of Kleppmann, Jahns, and the Ink & Switch team (the local-first collective) advanced the theory; the work of the Notion, Linear, Figma, Reflect, and Roam teams brought these ideas into large-scale production. In 2026, the patterns are mature enough for teams that are not GAFAM.

Notion deployed its multiplayer engine across all its blocks, making simultaneous editing fluid even on multi-megabyte pages. Linear pushed offline mode to the point of allowing the creation, editing, and reorganisation of tickets fully offline, with sync on network return. Figma industrialised Git-like branching on multiplayer graphic documents. Reflect and Roam built their product entirely on CRDTs, with no traditional server layer.

What these deployments demonstrate is not that local-first is easy — these teams invested person-years in their sync engines. It is that the entry barrier has dropped. Yjs and Automerge are stable, documented, and performant. y-sweet, hocuspocus, and y-websocket provide ready-to-use sync servers. y-indexeddb and y-protocols handle local persistence and server-side materialisation. For a team without Notion's resources, the assembly is now realistic.

The psychological threshold has been crossed: local-first is no longer a lab experiment; it is an architecture pattern seen in production at publishers of every size. The question is no longer "is it possible?" but "is it relevant for this product?".

The stack: IndexedDB, sync engine, server, materialisation

A typical local-first architecture in 2026 is organised in four layers. At the bottom, local persistence: IndexedDB (or OPFS for raw performance) stores the CRDT state. The y-indexeddb library connects a Yjs document to IndexedDB, letting the application re-read state instantly at startup, with no network fetch. This layer delivers the "it works right away, even offline" promise.

Above it, the sync engine. On the client side, the Yjs API (`awareness`, `updateV2`, `applyUpdate`) handles producing and applying updates. On the server side, two common options: y-websocket, lightweight and sufficient for an MVP, or y-sweet (maintained by Drifting in Space), more robust, with room management, persistence, and horizontal scalability. Hocuspocus, by the authors of Tiptap, is a solid alternative when the text editor sits at the heart of the product.

The third layer is optional but quickly becomes indispensable: server-side materialisation. A CRDT document is an operational structure, not a queryable one. To implement full-text search, an analytical dashboard, or a public API, you need a projection of the CRDT into a relational model. The y-protocols family (notably y-protocols/awareness and y-protocols/sync) provides tools for this; in practice, you write a subscriber that listens to updates, decodes the document to JSON, and writes to Postgres. That is what y-sweet does with its Rust backend, and it is the pattern we apply systematically.

The fourth layer, finally, is authentication and security. y-sweet and hocuspocus integrate with Supabase Auth via short-lived signed JWT tokens. The rule: never a server key on the client side, always an identity check at the sync server, and a per-document policy (who can read, who can write) evaluated before the WebSocket opens.

The hard parts: migrations, queries, partial sync, security

On paper, local-first chains together in four elegant layers. In production, four categories of problems come back systematically, and it is better to know them before committing.

First category: schema migrations on CRDTs. A relational database migrates with an atomic SQL script. A CRDT document is an operation history: changing the expected shape means reprocessing that history, or maintaining client-side versions. Yjs and Automerge offer strategies, but none is trivial. In practice, you avoid breaking schema changes, you version documents explicitly, and you plan a lazy-migration mechanism at decode time. It is less elegant than an `ALTER TABLE`, and you have to accept it.

Second category: server-side queries. You cannot run a `SELECT WHERE` against a CRDT serialised to binary. Any analytical interrogation, any public REST endpoint, any full-text search requires a materialised view. That means maintaining an update → decode → write-to-Postgres loop, handling eventual consistency (the view can lag by a few hundred milliseconds), and accepting extra operational complexity. It is the price to pay to get both offline-first and a classic server API.

Third category: partial sync — replication per collection. A user opening the app does not want to download their entire workspace onto their phone. They want their recent projects, not the 2022 archive. Implementing per-sub-collection replication means splitting documents, managing per-room subscriptions, and maintaining a per-device sync state. y-sweet eases this with its broad- and sub-document system, but it remains an area where you spend time.

Fourth category: security, notably the end-to-end encryption dream. Local-first lends itself naturally to E2EE: the encryption key lives on the devices, the sync server only sees opaque updates. It is elegant, but it collides directly with server-side search: you cannot index encrypted content in Postgres. You must choose between strict E2EE (client-side search only, over downloaded documents) and a decrypted server (centralised search, but the server sees the data). This trade-off is structural, and no configuration dodges it.

The 2026 SaaS pattern: online-first, local-first, or hybrid?

With these elements in mind, the architectural decision crystallises. Three profiles emerge in 2026, and the choice depends less on fashion than on an honest analysis of the application's load profile.

Online-first remains relevant for read-heavy applications: analytical dashboards, consolidated reports, admin interfaces, platforms where data changes little but must be read by many. Putting a CRDT behind a Metabase dashboard brings nothing but complexity. The rule: if the user reads more than they write, and always works connected, staying on a classic server architecture is the right choice. Engineering cost is minimal, tooling is mature, deployment is simple.

Local-first wins for write-heavy applications: note editors, task managers, field CRM, briefing tools, long-duration forms, collaborative design tools. Everywhere the user creates, edits, undoes, resumes — and where write latency kills the experience — local-first transforms the product. On these profiles, the engineering cost is paid back by the UX gain, and the competitive differentiation is real. A note editor that responds in 0 ms beats one that responds in 800 ms, whatever the features.

The hybrid model applies to complex collaborative applications in the Figma mould: the read part (component catalogue, shared libraries) on a classic server, the collaborative editing part (the canvas, the comments) on local-first CRDTs. It is the most powerful model but also the most demanding — you maintain both worlds and their interface. For most products, a clear online-first or local-first choice is healthier than a lukewarm mix. The decision matrix boils down to three questions: does the user write often? do they work offline? is real-time collaboration core or peripheral? From the answers, the architecture decides itself without ambiguity.

Key takeaways

Key points.

  • Local-first = data lives on the device first; the cloud becomes a sync peer, not an oracle.
  • CRDTs (Yjs / Automerge) make merging automatic: everyone can edit anything, conflicts converge mathematically.
  • 2026 stack: IndexedDB + Yjs + y-sweet (or hocuspocus) + Postgres materialisation via y-protocols.
  • Hard parts: CRDT schema migrations, server-side queries, per-collection partial sync, and the E2EE-vs-search trade-off.
  • 2026 decision: online-first for read-heavy (dashboards), local-first for write-heavy (notes, tasks), hybrid for Figma-like collaboration.
  • Notion, Linear, Figma, and Reflect democratised the patterns: local-first is no longer a lab topic.
Local-firstCRDTYjsAutomergeOffline-firstSaaS