The Sync Engine Grew Up: Local-First Became an Architecture Decision in 2026, Not a Research Project
Zero shipped 1.0, TanStack DB added persistence, and CRDTs stopped eating memory. Instant UI and offline capability are now library decisions rather than distributed-systems projects, which means the cost has moved somewhere most teams are not looking.
Why Did Local-First Stop Being a Research Topic This Year?
The idea is old enough to have a canonical essay. Ink & Switch published the local-first software paper in 2019, setting out seven ideals for applications where the user's own device holds the primary copy of the data and the network is a synchronisation channel rather than a dependency. For most of the following six years the honest answer to "should we build this way" was: only if you can afford to staff a distributed systems team, because the pattern was real and the tooling was not.
Three things landed close enough together to change that answer. Rocicorp shipped Zero 1.0 on 8 June 2026, the first stable release of its general-purpose web sync engine, after nearly two years of development and more than 50 releases. TanStack DB 0.6 added SQLite-backed persistence in March 2026, so local state survives an app restart rather than a tab, and does so across the browser, Node, React Native, Expo, Capacitor and edge runtimes including Cloudflare Durable Objects. And Automerge 3.0 cut memory usage by more than 10x by moving its columnar on-disk format into memory: pasting Moby Dick into an Automerge 2 document consumed 700MB, and in Automerge 3 it consumes 1.3MB, while a document that had not finished loading after 17 hours loads in 9 seconds.
That last one deserves a moment, because it is the quiet unblocker. The standard objection to CRDTs was never correctness, it was that document history grew without bound and eventually made the client unusable. A tenfold memory reduction does not make CRDTs the right answer for everything, but it removes the reason teams abandoned them at the prototype stage.
The ecosystem signal arrived in February. FOSDEM 2026 ran a dedicated devroom for local-first, sync engines and CRDTs, 23 talks across a full Sunday on 1 February, covering Yjs, Automerge, ElectricSQL, PouchDB and CouchDB, p2panda, Loro and OrbitDB. Conference tracks are a lagging indicator of adoption, not a leading one. They appear when enough people have already shipped something to argue about.
So the question facing an engineering leader in 2026 is no longer whether this is feasible. It is whether it is warranted for the product in front of you, and that turns out to be a much harder question, because the honest arguments for and against are product arguments rather than technical ones.
What a Sync Engine Actually Replaces in Your Codebase
Before evaluating any of these tools, it is worth writing down what they displace, because almost every team with a responsive product has already built a substantial fraction of a sync engine without ever calling it that.
The pile usually looks like this: a data-fetching layer with cache keys and invalidation rules, a set of hand-written optimistic update handlers, a retry queue for failed mutations, ad hoc conflict rules that live in whichever endpoint the conflict happens to reach, a WebSocket or polling channel for remote changes, a reconnection path, and a banner that tells users their connection is unstable. Each piece was added by a different engineer in a different quarter to fix a different complaint, and collectively it is the least specified and least tested subsystem in the product. Nobody designed it. It accreted.
The canonical reference implementation is worth studying precisely because it is unglamorous. A detailed breakdown of Linear's sync engine describes an in-memory object graph built on MobX, a transaction queue persisted to IndexedDB, a WebSocket event stream for remote updates, and last-write-wins conflict resolution, with CRDTs used only for issue descriptions. The product most often cited as proof that local-first works did not adopt the most sophisticated merge strategy available. It adopted the cheapest one that was correct for its data, and spent the saved effort on the parts users actually feel.
What users feel is latency, and the threshold has been known for a long time. The Doherty threshold, published in the IBM Systems Journal in 1982, puts the boundary at roughly 400 milliseconds: below it, interaction feels continuous and the user stays in flow; above it, attention breaks and the user re-evaluates whether the action was worth taking. A request-response architecture meets that bar through continuous optimisation work on a moving target of network conditions. A local read meets it structurally, on a train, in a lift, on hotel wifi, without anyone optimising anything.
The trade is therefore not complexity for simplicity. It is bespoke complexity you maintain for packaged complexity someone else maintains, plus a new set of problems you did not previously have. That is frequently a good trade. The rest of this piece is about the conditions under which it is not.
Four Architectures Hide Behind One Phrase
"Local-first" covers at least four genuinely different architectures, and most bad adoption decisions come from picking a product before picking a category.
The first is CRDT document sync, the world of Yjs and Automerge. The unit is a document, merges happen without a server arbiter, and concurrent edits converge by construction. This is the right shape for collaborative text, canvases, diagrams and anything where two people editing the same thing at the same time is the point rather than an edge case. It is the wrong shape for data that is fundamentally relational and queried, because you will end up rebuilding query semantics on top of a document.
The second is query-driven sync from an existing Postgres database. ElectricSQL syncs normalised shapes out of Postgres incrementally while TanStack DB handles local query execution, optimistic state and reactive updates. Zero takes a related path with its own query language, ZQL, running against a cache service that maintains a read-only Postgres replica, so queries resolve locally first and reconcile in the background. Both suit products whose data already lives in a relational database and whose developers want to keep thinking in queries.
The third is client-database sync, best represented by PowerSync, which watches the backend database's change stream, filters changes through sync rules, and pushes them to clients that operate on a local SQLite database and write back through a developer-defined upload endpoint. The explicit upload endpoint is often read as a weakness and is usually a strength: it is where your validation, authorisation and business rules stay on the server, where they belong.
The fourth is the server-authoritative reactive backend, the Convex shape, where you get live-updating queries and optimistic writes without ever ceding authority over the data to the client. It is the pragmatic middle ground, and for transactional products it is frequently the correct answer even though it is the least fashionable one.
The category follows from one question, and it is a business question rather than an engineering one: when two people change the same thing while one of them is offline, who is allowed to win? If the answer is "whoever wrote last, and nobody will care", last-write-wins on a synced table is sufficient and you should not pay for anything more. If the answer is "both, merged", you are in CRDT territory. If the answer is "neither, the server decides after checking stock levels and credit limits", you want server authority and you should stop reading vendor comparisons. Most products contain all three answers in different tables, which is why the decision belongs in a data model review rather than a framework bake-off.
Key Takeaways
- CRDT document sync fits collaborative editing, not relational querying
- Query-driven sync from Postgres suits products whose data already lives in a relational store
- Client-database sync keeps validation on an explicit server upload endpoint, which is a feature
- Start from conflict semantics per table, not from a vendor shortlist: most products need more than one
Where It Pays, and Where It Is a Trap
The strongest case is work that happens where connectivity does not. Field service is the clearest example: one field-service integrator's analysis of offline-first operations puts 40% to 43% of service calls in areas with poor or no coverage, and reports 20% to 30% productivity gains and roughly 40% lower administrative time for teams that modernise around mobile workflows. Basements, plant rooms, rural sites, ships, tunnels and hospitals are not edge cases in those businesses. They are the business, and an application that cannot complete a work order without a signal is an application that generates paper.
The second strong case is genuine multiplayer. If two users editing the same object simultaneously is a normal day rather than a rare collision, you need merge semantics whether or not you adopt a sync engine, and adopting one means you are using a library that has been attacked by more edge cases than your own implementation ever will be.
The third is the high-interaction internal tool: issue trackers, dispatch boards, CRMs, warehouse consoles, clinical worklists. These are applications someone uses for six hours a day, where the difference between a 90-millisecond interaction and a 600-millisecond one compounds across several hundred interactions per shift and shows up as user sentiment that nobody can trace back to a specific ticket.
Now the traps. Money and inventory are the first: ledgers, payments, stock and anything with a limit need an authoritative server that can reject a write, not a merge strategy that reconciles two truths. The second is permission-dense data, where every user sees a different filtered slice. Partial sync under a real row-level security model is the single hardest part of every sync engine adoption, it is the part the vendor demos skip, and it is where evaluations that looked finished in week two discover another quarter of work in week six.
The third trap is volume. If a single user's working set is hundreds of thousands of rows, you are designing a replication strategy rather than adopting a library, and you should cost it as such. The fourth is regulatory, and it is the one engineering teams notice last: syncing personal data onto user-controlled devices moves that data into scope for device loss, remote wipe, retention limits and subject access requests in a way a server-only architecture does not. That is a solvable problem and a genuine one, and it belongs in the design review rather than the security review three weeks before launch. We have written separately about where production data is allowed to travel when development is distributed, and the same reasoning applies to devices.
Key Takeaways
- Offline field work, genuine multiplayer and all-day internal tools are where the economics are clear
- Ledgers, stock and anything with a limit need server authority, not merge semantics
- Partial sync under a real permission model is the hardest and most commonly underestimated piece
- Personal data on devices changes your retention, wipe and subject-access obligations
The Bill Nobody Budgets: Eviction, Migrations and the Untested Path
Assume you have picked correctly and the product genuinely benefits. There are four costs that appear on no vendor comparison page and reliably surprise teams in the second quarter of the work.
The first is that your local database is not durable, at least on the web. WebKit's tracking prevention deletes all script-writable storage after seven days of browser use without a user interaction with the site, and script-writable storage means IndexedDB, localStorage, sessionStorage and service worker registrations together. A user who takes a fortnight of holiday comes back to an empty local store. This does not break a correctly built application, because a cold resync is a normal path, but it does mean the cold path must be a first-class supported experience with a sensible loading state rather than a rarely exercised branch that shows a spinner for forty seconds on a large account.
The second is client-side schema migration, which is the part that genuinely has no server-side equivalent. You are now shipping a database to machines you do not control, running application versions you cannot force anyone to upgrade. Every schema change has to be forward-compatible for clients that have not updated, every migration has to run on device against data that may be arbitrarily stale, and you need a way to invalidate a client generation that turns out to be wrong. Teams that have only ever migrated a server database consistently underestimate this, because the server version of the problem is one machine and one migration window.
The third is that these libraries are young and their limits land on product decisions. The 1.0 coverage of Zero notes a client bundle of 718KB uncompressed and 232KB gzipped, PostgreSQL as the only supported database, views that do not sync, some column types including arrays unsupported, and no server-side rendering support yet. None of those is a defect for a 1.0 release. All of them are things a product manager needs to hear before a launch date is promised, especially the rendering one if your growth strategy depends on server-rendered pages.
The fourth is testing, and it is the one that bites in production. The offline path is, in most codebases, the least exercised code in the product, because conventional end-to-end suites either mock the storage layer or bypass it entirely, so the suite is green while the behaviour is untested. Exercising this properly means two clients editing concurrently, forced network partitions in CI, storage quota exhaustion, private browsing mode, multiple tabs of the same origin writing at once, and a resync from empty against a realistically large account. Then it means telemetry for sync lag and queue depth in production, because "the app is slow" and "my changes from this morning have not appeared for my colleague" are different incidents with different causes, and only one of them shows up in your APM dashboard. This is the kind of verification capacity that has been quietly cut in a lot of organisations, a pattern we covered in the piece on the verification gap.
Vendor Risk Belongs on the Architecture Diagram
A sync engine is not a library you can swap on a wet afternoon. It sits between your database and your UI, it shapes your data model, and it owns your write path. That makes the maturity and ownership of the project a first-class architectural concern rather than a procurement footnote, and this ecosystem has already produced two instructive examples.
Triplit, a well-regarded sync engine with an enthusiastic user base, joined Supabase on 8 October 2025. The announcement is candid and worth reading in full: the intention is not to integrate Triplit into the platform but to have its co-founder work on integrations with other syncing systems including ElectricSQL, Zero and PowerSync, while the Triplit codebase is further open-sourced. Supabase's own framing of the problem is the useful part, describing solving offline mode in a way that works for everyone as a formidable challenge. Nobody did anything wrong here, and adopters still woke up with a community-maintained dependency in the middle of their write path.
ElectricSQL provides the other example. The project announced a clean rebuild in July 2024 and stopped development on the original CRDT-based version, moving to a simpler sync model. The rebuild was, by most accounts, the right engineering call. It was also a rewrite for anyone who had shipped on the previous architecture.
Practitioner reports make the same point from ground level. One 2026 evaluation of the field describes roughly two months spent on an ElectricSQL and TanStack DB integration before abandoning it over the sync push mechanism and the custom backend endpoints required for client writes, rejecting LiveStore because its model maps one user to one SQLite instance and made sharing data between users architecturally awkward, and settling on Zero, which worked, but does not include real-time presence, so cursors and chat need separate infrastructure. That is the texture of a real evaluation, and it is not information you can get from a feature matrix.
Four rules follow from that, and they cost very little to apply. Keep your authoritative database authoritative and boring, so that the sync engine is a projection you can replace rather than a system of record you cannot. Wrap the engine behind your own data-access module rather than letting its query language spread into six hundred components, which is the same discipline that makes any infrastructure choice reversible. Prefer engines you can self-host, because a hosted sync service is a hard dependency on someone else's uptime for your users' ability to type. And run a genuine two-week spike against your real permission model and a realistically sized account before committing, not against the collaborative to-do list in the getting-started guide, because every one of these tools is excellent at the to-do list.
Key Takeaways
- The sync engine owns your write path, which makes project maturity an architectural concern
- Two prominent projects have already changed ownership or rebuilt their core architecture
- Keep the authoritative database boring and treat the synced store as a replaceable projection
- Spike against your real permission model and data volume, never against the demo app
Agents Turned Local State Into a Backend Problem Too
There is a second force pushing these tools forward that has nothing to do with offline field work, and it is the reason the category is likely to keep consolidating rather than fade: AI agents need durable, observable, shared state, and that turns out to be the same problem a sync engine already solves.
The mechanics are visible in the tooling. Because TanStack DB's persistence layer now runs in Node and in edge runtimes as well as the browser, the same collection can exist on both sides of the connection. ElectricSQL's own write-up of the release describes the pattern explicitly: persist jobs or generations in a collection, define a query for the items ready to run, and use lifecycle effects to trigger the next step, so that state lives in one store, workflows react to query results, and the interface updates from the same source of truth as the worker.
That matters because agent work breaks the request-response assumption harder than any human workflow does. A user action that takes eleven seconds is a spinner problem. A user action that dispatches a task running for eleven minutes across several tool calls, with intermediate results worth showing, is a state problem, and building it on request-response means inventing polling, a job table, a notification channel and a reconciliation path, which is to say inventing two-thirds of a sync engine again under deadline pressure. The durability half of this question is a genuinely separate decision, and we worked through it in the piece on durable execution for long-running agents.
The caveat is worth stating plainly, because the enthusiasm here runs ahead of the evidence. If your agent produces one result and writes it to a table that one user reads, a queue and a WebSocket are the correct answer and a sync engine is an expensive way to feel modern. The pattern earns its place when multiple participants, human and automated, are reading and writing overlapping state concurrently and everyone needs to see a coherent view. That is a real and growing class of product, and it is not yet most products.
This Is a Senior Architecture Call, Not a Library Swap
Look at what the preceding sections actually ask for. Data modelling that can articulate conflict semantics per table. Frontend engineering comfortable with reactive local stores and optimistic state. Backend engineering that can express a permission model as a partial sync specification without leaking rows. Mobile engineering that understands on-device storage and its eviction behaviour. Release engineering for client-side schema migrations against installs you cannot force to upgrade. And QA that can construct a two-client partition test rather than mocking the storage layer. That is five disciplines and a decision-maker, and the reason so many of these projects stall at 80% is that most organisations have three of the five.
The failure pattern is consistent enough to predict. A capable engineer builds a prototype in a fortnight that is genuinely delightful, the demo lands, the date goes in the roadmap, and the project then spends two quarters on permissions, migrations, the account with 200,000 rows, and the iOS user whose local store keeps disappearing. None of that is research. All of it is delivery, and delivery over a sustained period is what a standing team is for.
This is one of the clearer arguments for a dedicated development team over a fixed-scope engagement, for a structural reason rather than a commercial one. A sync architecture is not a feature you finish. Every new entity added to the product for the next three years has to answer the conflict-semantics question, every permission change has to be reflected in the sync specification, and every engine upgrade has to be assessed against client versions still in the field. You want the people who decided that comments would be last-write-wins and descriptions would be CRDTs to still be there when someone proposes a feature that breaks that assumption, which is the general case we set out in dedicated teams versus project-based outsourcing.
It is also a strong argument for nearshore development in particular, because the decisive questions here are conversations rather than tickets. Whether a dispatcher's reassignment should beat a technician's offline status update is a product call with an engineering cost attached, and it does not survive being written into a ticket and answered the following afternoon from nine timezones away. Our engineering teams in Serbia work a full business day that overlaps European hours and most of the US morning, which is the difference between resolving that trade-off in a call and deferring it to next sprint, where it will be resolved by whoever writes the code. For clients weighing that geography against the alternatives, we compared them directly in Serbia, Poland and Romania as nearshore destinations, and the timezone argument itself gets its own treatment in the hidden line item in every outsourcing quote.
Where the product knowledge already sits in-house and what is missing is a specific capability, usually a frontend or mobile engineer who has actually shipped a synced store rather than read about one, staff augmentation into your existing squad is the faster route and the cheaper one. Where the work is a field application from the ground up, our mobile development practice treats offline capability and conflict resolution as part of the initial architecture rather than a phase-two retrofit, which is the only point at which they are affordable. And if the immediate need is capacity on an existing React codebase heading in this direction, you can hire React developers straight into a team you already run.
Key Takeaways
- The work spans data modelling, frontend, backend permissions, mobile storage, release engineering and QA
- Projects stall on delivery problems: permissions, migrations, large accounts and storage eviction
- A sync architecture needs continuous ownership because every new entity re-asks the conflict question
- The decisive calls are product conversations, which is where working-hours overlap pays for itself
Pick the Conflict Semantics Before You Pick the Library
The interesting thing about local-first in 2026 is not that it works. It has worked for years in a handful of products with the engineering budget to build a sync engine from scratch. What changed is that stable, general-purpose engines arrived, the CRDT memory problem was solved, and persistence stopped stopping at the edge of the browser tab, which together moved this from a capability you build to a capability you adopt. That is a genuine shift, and it is also where teams go wrong, because adopting a sync engine looks like a library decision and behaves like a data architecture decision. The library is reversible in a quarter. The choice of who wins when two people change the same record while one of them is in a basement is not, because it propagates into every entity, every permission rule and every product decision that follows. Answer that question first, per table, in a room with the people who understand the business rules. Then evaluate two engines against your real permission model and your largest account, budget for the migration and testing work that no comparison page mentions, and treat the offline path as a supported experience rather than a fallback. Teams that do it in that order ship something that feels instant. Teams that do it in the other order ship a prototype that feels instant, and then spend two quarters discovering what their data actually required.
Building a team in Eastern Europe?
StepTo helps European and US companies build senior-led nearshore engineering teams in Serbia. Let's talk about what your next engagement could look like.
Start a conversationWritten by
Igor GazivodaCo-founder & CEO · StepTo
Igor has 15+ years in software engineering and business development. Former CTO at a Series A fintech startup, he specializes in scaling engineering teams, nearshore strategy, and AI-driven product development. He holds a Master's in Computer Science from the University of Belgrade and has published on distributed systems architecture.
LinkedIn →