Why Local-First Is Having a Moment in 2026
The promise of local-first has been circulating since Ink and Switch published their manifesto in 2019, but 2025 was the year it stopped being theoretical. Linear proved you could build a billion-dollar product on local-first principles. Figma demonstrated that real-time collaboration at massive scale was possible without traditional request/response architecture. And a wave of new sync engines made it feasible for teams without PhDs in distributed systems to adopt the pattern.
In 2026, local-first is not just viable. It is increasingly the default for any app where responsiveness, offline capability, or real-time collaboration matters. The reason is simple: users have been trained by native apps to expect instant interactions. A 200ms round trip to a server feels sluggish compared to a 2ms local read. And when your competitor ships instant UI while you are still showing spinners, you lose.
Three frameworks are competing for dominance in the JavaScript and TypeScript local-first ecosystem: TanStack DB, ElectricSQL, and Zero. Each represents a fundamentally different bet on how sync should work. TanStack DB is a client-side reactive database with pluggable sync backends. ElectricSQL streams Postgres data directly to clients via shape subscriptions. Zero (from Rocicorp, the team behind Replicache) delivers zero-latency reactive queries through a server-authoritative cache architecture.
This is not a surface-level feature matrix. We have deployed all three in production at Kanopy Labs and have strong opinions about when each one shines and when it falls apart. If you are new to the broader pattern, start with our local-first software architecture guide for foundational concepts.
TanStack DB: Client-Side Reactive Database with Pluggable Sync
TanStack DB comes from Tanner Linsley, the creator of TanStack Query (formerly React Query), TanStack Router, TanStack Table, and the rest of the TanStack ecosystem. It launched in 2025 as an embedded client-side reactive database, described as "the reactive client store for your API." It sits on top of TanStack Query and extends it with collections, live queries, and transactional mutations.
Architecture and Core Concepts
TanStack DB organizes data into collections, which are typed, normalized in-memory data stores that function like client-side tables. Collections can be populated from REST APIs, GraphQL endpoints, ElectricSQL shapes, PowerSync, Firebase, or custom sync adapters. You define your schema in TypeScript, and TanStack DB gives you reactive queries over those collections that automatically update your UI when the underlying data changes.
The query engine is the technical standout. TanStack DB uses a differential dataflow engine (called d2ts, exposed via @tanstack/db-ivm) that incrementally recomputes query results when data changes. Rather than re-running entire queries, it computes only the affected parts of the result set. The team benchmarks this at sub-millisecond update times: updating one row in a sorted 100,000-item collection completes in roughly 0.7ms. Live queries support a SQL-like fluent API with from, join, where, select, orderBy, and limit, and you can join across collections from different sync backends.
The key architectural decision is that TanStack DB is sync-backend agnostic. Official collection adapters include @tanstack/query-db-collection (TanStack Query for REST/GraphQL), @tanstack/electric-db-collection (ElectricSQL), and @tanstack/powersync-db-collection (PowerSync). Three sync modes are available: eager (loads entire dataset upfront), on-demand (loads data based on active query predicates via predicate pushdown), and progressive (fetches the requested subset first, then syncs the rest in the background).
Optimistic Mutations and Offline Support
Writes happen locally first through transactional optimistic mutations. When a user creates or updates a record, it appears in the UI immediately. TanStack DB tracks pending mutations and reconciles them once the server confirms. If the server rejects a mutation, the client state rolls back automatically. The server is the source of truth for conflicts, with transaction IDs enabling rebasing of optimistic state over concurrent writes from other users.
Offline support was added in v0.6 via SQLite-backed persistence using wa-sqlite with Origin Private File System (OPFS) in browsers. Persistence works across browser, React Native, Expo, Node, Electron, Capacitor, Tauri, and Cloudflare Durable Objects. The @tanstack/offline-transactions package implements an outbox-first pattern for sequential mutation replay when connectivity returns.
What Sets It Apart
- Ecosystem integration: If your app already uses TanStack Query, Router, and Table, TanStack DB slots in naturally. You can adopt it incrementally, collection by collection, without changing your server architecture.
- Framework breadth: Official adapters for React, Vue, Solid, Svelte, and Angular. This is the broadest framework support of any comparable sync solution.
- Mix-and-match sync: Different collections can use different data sources (one from REST, another from ElectricSQL) and be joined together in live queries. No other tool in this comparison supports this.
- License: MIT open source. No commercial tiers, no per-seat pricing, no usage limits.
Limitations
TanStack DB is still in beta (v0.1.x as of mid-2026). The maintainers explicitly caution that it should be considered experimental. Conflict resolution defaults to server-validated rollback, with no built-in CRDT support. The sync adapter ecosystem is growing but still maturing. You will spend time building or configuring your own sync layer, which is work that ElectricSQL and Zero handle more completely out of the box. When persistence is enabled, the wa-sqlite WASM bundle adds meaningful weight to the client. Documentation is improving rapidly but thinner than the other two.
Maturity verdict: Promising and under very active development (over 1,100 releases), but still beta. Suitable for new projects where you accept some API instability. For mission-critical production apps, monitor the path to 1.0 closely.
ElectricSQL: Postgres-Native Sync with Shapes
ElectricSQL takes the most database-centric approach of the three. The premise is compelling: if your app already runs on Postgres (and most apps do), why not sync Postgres data directly to clients instead of building a separate API layer?
Architecture and Sync Model
ElectricSQL underwent a major architectural rebuild in mid-2024 ("Electric Next") and reached 1.0 GA in March 2025. The current version is a focused read-path sync engine. You run the Electric sync service (an Elixir application distributed as a Docker image) alongside your Postgres instance. It connects via logical replication, reads the write-ahead log (WAL), and streams changes to connected clients over plain HTTP. This HTTP-native design means shape data is cacheable by CDNs, proxies, and standard HTTP infrastructure, enabling Electric to scale to over one million concurrent clients on a single Postgres instance.
The core API primitive is the Shape. A shape defines a subset of a single Postgres table that a client subscribes to. You specify the table, an optional WHERE clause for filtering, and optional column selection. The Electric service monitors the WAL for changes matching your shape definition and pushes incremental updates to clients via long-polling or Server-Sent Events. Clients track their own offset and resume seamlessly after disconnection.
Write Path: Bring Your Own
This is the most important thing to understand about modern Electric: it does not prescribe a write path. The pre-2024 version had built-in CRDTs and bidirectional active-active replication. The current architecture intentionally dropped those in favor of simplicity and stability. You choose your own write pattern, and Electric provides several recommended approaches:
- Online writes: Standard API calls (REST, GraphQL, tRPC) to your server. Simplest option.
- Optimistic state: Component-scoped optimistic UI using
useOptimisticfor instant feedback while the server processes writes. - Persistent optimistic state: Shared optimistic state with localStorage or Valtio that survives page navigation and supports rebasing on concurrent server updates.
- Through-the-database sync: Full local-first pattern using PGlite as a local Postgres replica with shadow tables and changelog-based sync. This is the heaviest approach but gives you true offline writes with conflict resolution.
Postgres Integration Depth
This is where ElectricSQL genuinely differentiates. Your Postgres schema is the single source of truth. Migrations run on Postgres, and the sync service follows automatically. Row-level security policies, constraints, and indexes all apply to shape filtering. Shapes support WHERE clauses with comparison operators, logical operators, IN, BETWEEN, and array operators. If you have 10 years of Postgres expertise on your team, Electric lets you leverage every bit of it.
Key Metrics
- Bundle size: The
@electric-sql/clientTypeScript library is lightweight (under 50 KB gzipped). If you include PGlite for in-browser Postgres, add approximately 3.7 MB gzipped. PGlite is optional, and many teams skip it in favor of lighter client storage. - Sync latency: Sub-15ms end-to-end for Postgres-to-client change delivery. Initial shape loads scale with data volume.
- Self-hosting: Fully self-hostable. Apache 2.0 licensed. Requires Postgres 14+ with logical replication enabled.
- Pricing: Electric Cloud (managed hosting) uses pay-as-you-go pricing: $1 per million writes, with Postgres Sync at $3 per million writes. Bills under $5/month are waived. Pro tier at $249/month includes a 10% usage discount. Scale tier at $1,999/month includes 20% discount.
Limitations
ElectricSQL requires Postgres. If you are on MySQL, MongoDB, or DynamoDB, this is not an option. Shapes are single-table only (no JOINs, though subqueries can handle cross-table filtering). The write path is entirely your responsibility, which means more integration work compared to Zero's batteries-included approach. The PGlite dependency adds significant weight if you want full SQL on the client. The shapes API went through breaking changes in 2024 and 2025 before stabilizing at 1.0, so make sure you are reading current docs. React hooks are available via useShape, with integrations for TanStack DB, Phoenix LiveView, and MobX.
Zero: Server-Authoritative Sync with Zero-Latency Reads
Zero is Rocicorp's third-generation sync engine, following Replicache and Reflect. Where Replicache required you to implement push and pull endpoints on your server and handle partial sync yourself, Zero provides a complete, batteries-included sync solution backed by Postgres. It reached version 1.0 in June 2026 after roughly two years of development.
Architecture and Core Model
Zero's architecture centers on zero-cache, a stateful middleware server that sits between your Postgres database and your clients. It maintains a SQLite replica of your upstream Postgres data, connects via logical replication for change detection, and serves clients over WebSockets. When a client subscribes to a query, zero-cache materializes the result, then pushes row-level diffs (adds, removes, edits) incrementally as the underlying data changes.
Queries in Zero use ZQL (Zero Query Language), a TypeScript-native streaming query language with a builder pattern API. ZQL supports where clauses, ordering, limits, related data (joins), compound filters, and relationship existence checks. When you write useQuery in your React component, ZQL first resolves against the local cache (returning data in the next frame), then hydrates from the server in the background. If the server has newer data, the cache updates and your component re-renders. The experience is instant reads with server-authoritative writes.
Server-Authoritative Conflict Resolution
This is the biggest philosophical difference from ElectricSQL's read-path model. Zero is explicitly server-authoritative: the server always wins. When you make an optimistic mutation, Zero applies it locally for instant feedback, but the mutation is sent to the server for validation. Mutators are TypeScript functions that run on both client (optimistically) and server (authoritatively). The server mutator executes against the real database with full access to current state and authenticated user context. If the server rejects a mutation, the client's optimistic update rolls back silently. This transactional conflict resolution model is more nuanced than simple last-write-wins, because you can implement any business logic you want in your server-side mutator.
Postgres Integration
Zero requires Postgres 15+ with logical replication enabled. It connects directly to Postgres (no connection pooling) and uses event triggers and logical replication for change detection. Your Postgres schema is the source of truth, but clients interact with ZQL rather than raw SQL. Supported providers include AWS RDS, Aurora, Neon, Google Cloud SQL, and PlanetScale for Postgres. Supabase and Fly.io have partial support. Current limitations include no support for Postgres views, array column types, or JSON-based filtering.
Key Metrics
- Bundle size: Approximately 232 KB gzipped for the client library. No WASM dependencies, but heavier than TanStack DB's core.
- Query latency: Under 5ms for cached queries. This is the headline number and it delivers on it.
- Sync latency: 50 to 200ms for incremental updates on a good connection. Server round-trip required for authoritative writes.
- Self-hosting: Fully self-hostable (Apache 2.0 license). The zero-cache server has two components: a Replication Manager (single instance, must be private) and View Syncers (horizontally scalable, publicly accessible). Docker images provided.
- Pricing: Open source core is free forever. Managed hosting: Hobby at $30/month (10 GB storage), Professional at $300/month (100 GB, dedicated resources, SLA), BYOC at $1,000+/month (self-hosted in your AWS account).
Limitations
The most important limitation: Zero does not support offline writes. When the client is disconnected, reads from cached data continue to work, but writes are rejected. During the brief "connecting" state (covering server restarts and momentary network drops), writes are queued. But extended offline usage with write capability is not supported. Rocicorp made this decision intentionally, arguing that offline writes in collaborative apps create inherent complexity that they chose not to take on. If your app needs users to work offline for hours and sync back later, this is a dealbreaker. Zero's React and SolidJS bindings are excellent, with community-maintained Vue and Svelte adapters available. Angular support is not available. The Replication Manager is a single point of failure that cannot be horizontally scaled. Notable missing features as of 1.0 include aggregation functions, server-side rendering, and column-level permissions.
Head-to-Head Comparison: The Details That Matter
Feature matrices are useful as a starting point, but the devil is in the details. Here is how these three frameworks compare across the dimensions that actually affect your shipping velocity and production reliability.
Sync Architecture
TanStack DB: No built-in sync protocol. You choose your own sync layer via collection adapters (TanStack Query for REST/GraphQL, ElectricSQL, PowerSync, or custom). This is maximum flexibility at the cost of integration work. Think of TanStack DB as the client-side reactive data layer that plugs into whatever sync transport you prefer.
ElectricSQL: Read-path sync built on Postgres logical replication. The Electric sync service streams shape data to clients over HTTP (CDN-cacheable). Writes are your responsibility: you can use standard API calls, optimistic state patterns, or the full through-the-database approach with PGlite for true local-first writes. This separation of read and write paths gives you flexibility but requires more integration work than Zero.
Zero: Full bidirectional sync with a server-authoritative model. The zero-cache middleware handles both reads (via incremental view maintenance) and writes (via mutators that run on both client and server). The server is the single source of truth. Clients cache aggressively and mutations are validated server-side. Simplest mental model of the three for apps that are primarily online.
Conflict Resolution
TanStack DB: Server-validated rollback. Optimistic mutations apply locally, the server validates, and rejected mutations roll back automatically. Transaction IDs enable rebasing over concurrent writes. For collaborative editing, you can layer in Yjs or Automerge.
ElectricSQL: Depends on your write pattern. If you use online writes (direct API calls), conflict resolution is standard server-side logic. If you use the through-the-database pattern with PGlite, you handle merge logic in your sync layer. The pre-2024 CRDT-based conflict resolution was removed in the architectural rebuild. Electric gives you the primitives but you design the strategy.
Zero: Transactional server-side resolution. Mutators are TypeScript functions that run on the server with full access to the current database state and authenticated user context. The server can implement any conflict resolution logic: last-write-wins, merge, reject, or custom business rules. Rejected mutations silently revert on the client (a known UX concern, as the client API currently lacks error feedback for rejected mutations).
React and Framework Bindings
TanStack DB: Broadest framework support. Official adapters for React (useLiveQuery), Vue, Solid, Svelte, and Angular (injectLiveQuery). The API follows TanStack Query conventions, so the learning curve is minimal for existing users.
ElectricSQL: React hooks available via useShape. Official integrations also exist for TanStack DB, Phoenix LiveView, MobX, and Yjs. The API stabilized at 1.0 but earlier versions had breaking changes, so verify you are reading current docs.
Zero: First-class React and SolidJS support with useQuery hooks that feel native. Community-maintained adapters for Vue and Svelte. No Angular support. The React developer experience is excellent, but the framework coverage is narrower than TanStack DB's.
Offline Support Quality
TanStack DB: SQLite-backed persistence via wa-sqlite with OPFS. The @tanstack/offline-transactions package provides an outbox-first pattern for queuing mutations offline. Works across browser, React Native, Expo, Electron, Capacitor, and Tauri. The offline story is solid but requires assembly.
ElectricSQL: Strongest offline read story. Once synced, shape data is available locally with zero latency. For offline writes, the through-the-database pattern with PGlite gives you a full local Postgres replica with shadow tables and changelog-based sync. This is the most capable offline write path, but it requires the heavyweight PGlite dependency and careful implementation.
Zero: This is where Zero draws a clear line. Offline reads work (cached data is available), but offline writes are rejected. During brief connectivity interruptions ("connecting" state), writes are queued, but in a fully disconnected state, writes fail. Rocicorp made this decision intentionally, prioritizing online experience simplicity over offline write complexity. If your users need to write while offline, Zero is not the right choice.
Bundle Size Comparison
This matters more than most teams realize, especially for mobile web where every kilobyte affects time-to-interactive.
- TanStack DB: Core library is lightweight (tree-shakeable, no WASM by default). When persistence is enabled, the wa-sqlite bundle adds additional weight. Lightest option without persistence.
- Zero: Approximately 232 KB gzipped. No WASM dependencies, but this is a substantial client library. Acceptable for most desktop web apps, potentially a concern for mobile web.
- ElectricSQL: The
@electric-sql/clientis under 50 KB gzipped. But if you add PGlite for full local SQL, that is another 3.7 MB gzipped. Without PGlite (using Electric as a pure read-path sync), the client is the lightest of the three.
For a more granular comparison of the client-side database options that underpin these sync engines, see our ElectricSQL vs Replicache vs TanStack DB breakdown.
Self-Hosting, Pricing, and the Total Cost of Ownership
The sticker price of a sync engine is the least interesting part of the cost equation. What matters is total cost of ownership: licensing, hosting, engineering time to integrate, ongoing maintenance, and the cost of migrating away if you make the wrong choice.
TanStack DB
MIT licensed, zero cost for the library itself. But you pay in engineering time. Building a production-grade sync layer on top of TanStack DB takes 2 to 6 weeks of senior engineering time, depending on your requirements. If you pair it with TanStack Query and a REST API, the sync is straightforward but not truly local-first (you are doing optimistic updates over HTTP, which is a step down from real bidirectional sync). If you pair it with ElectricSQL as a sync backend, you get the best of both worlds but inherit ElectricSQL's infrastructure costs.
ElectricSQL
Open source core (Apache 2.0). Self-hosting means running the Electric sync service (Docker image) alongside Postgres 14+. On AWS, a minimal production setup (Electric service on ECS plus RDS Postgres) runs approximately $80 to $150/month. Electric Cloud uses usage-based pricing: $1 per million writes for the core service, $3 per million writes with Postgres Sync. Bills under $5/month are waived entirely. The Pro tier at $249/month adds a 10% usage discount and supports up to 50 databases. The Scale tier at $1,999/month includes a 20% discount and 200 databases. Reads, egress, fan-out, and concurrent users are always free.
Zero
Fully open source (Apache 2.0). Self-hosting requires running zero-cache (a Replication Manager instance plus one or more View Syncers) alongside Postgres 15+. On AWS, a minimal production setup runs approximately $100 to $200/month for infrastructure. Zero's managed hosting offers three tiers: Hobby at $30/month (10 GB storage, shared resources), Professional at $300/month (100 GB, dedicated vCPU, SLA), and Bring Your Own Cloud at $1,000+/month (deployed in your AWS account with Rocicorp management).
Hidden Costs to Watch
- Migration cost: Switching between these frameworks is expensive. Budget 200 to 500 engineering hours for a migration between any two of them. Your data model, sync patterns, and conflict resolution logic are deeply coupled to your choice.
- Debugging cost: Sync bugs are the hardest bugs. When data diverges between client and server, diagnosing the root cause requires understanding the sync protocol. ElectricSQL and Zero both provide debugging tools, but the learning curve is steep. TanStack DB offloads this problem to whatever sync layer you chose.
- Scaling cost: All three scale differently. ElectricSQL's HTTP-native architecture with CDN caching can scale to over one million concurrent clients on a single Postgres instance for reads. Zero's View Syncers scale horizontally, but the Replication Manager is a single instance. TanStack DB scales based on your backend, which you control.
The bottom line: TanStack DB is cheapest upfront but most expensive in engineering time. ElectricSQL is the most cost-effective for read-heavy Postgres workloads (reads are free on Electric Cloud). Zero is the most complete out-of-the-box solution but has the highest managed hosting costs for production workloads.
Migration Paths from Traditional Backends
Most teams considering local-first sync are not starting from scratch. They have an existing REST or GraphQL API, a traditional database, and a frontend that fetches data the old-fashioned way. The migration path matters enormously.
Migrating to TanStack DB
This is the gentlest migration. If you already use TanStack Query, you can adopt TanStack DB incrementally. Start by converting one entity (say, your "todos" or "projects" data) to a TanStack DB collection. Keep TanStack Query as the sync transport. Your existing API endpoints continue to work. Over time, you can move more entities to collections and optionally swap in a more sophisticated sync backend. The risk is low because you are not changing your server architecture at all.
Migrating to ElectricSQL
Medium difficulty. If you are on Postgres, the server-side setup is adding the Electric sync service and configuring logical replication. Your existing tables, schemas, and migrations carry over. The client-side migration is more significant: you need to replace API calls with shape subscriptions and set up a local database (PGlite or SQLite). The hardest part is rethinking data access patterns. Instead of "fetch this endpoint," you think "subscribe to this shape." Budget 4 to 8 weeks for a full migration of a medium-complexity app.
Migrating to Zero
Similar difficulty to ElectricSQL. Server-side, you deploy the Zero sync server and connect it to your Postgres instance. Client-side, you replace API calls with Zero queries and mutations. The conceptual shift is smaller than ElectricSQL because Zero's server-authoritative model is closer to traditional client-server architecture. Your server-side business logic (validation, authorization, complex mutations) stays on the server where it belongs. Budget 3 to 6 weeks for a medium-complexity migration.
Gradual Adoption Strategy
All three support gradual adoption, but TanStack DB is the clear winner here. You can run TanStack DB alongside your existing data fetching with zero conflicts. ElectricSQL and Zero both require infrastructure changes (new sync services, Postgres configuration) that affect your entire stack even if you are only using them for a subset of your data. Our offline-first mobile apps guide covers additional patterns for progressive migration.
Recommendation Matrix: Which Sync Engine Fits Your Project
After building with all three in production, here is our honest recommendation framework. No single tool wins every scenario.
Choose TanStack DB When
- Your team already uses TanStack Query, Router, or Table and wants consistency across the stack
- You need a lightweight client-side data layer without committing to a specific sync architecture
- Your app has simple sync requirements (optimistic updates over REST/GraphQL are sufficient)
- Bundle size is a hard constraint (mobile web, embedded webviews)
- You want MIT open source with no licensing surprises
- You plan to evaluate sync backends later and want to avoid lock-in now
Choose ElectricSQL When
- You are running Postgres and want to leverage your existing schema, migrations, and expertise
- Read-path sync is your primary need (streaming Postgres data to clients in real time)
- You want to compose your own write strategy (online writes, optimistic patterns, or full through-the-database sync with PGlite)
- You need offline read capability with optional offline write support via PGlite
- You want full self-hosting with no dependency on a third-party cloud service (Apache 2.0)
- Real SQL on the client via PGlite is valuable for your use case (complex queries, joins, aggregations)
Choose Zero When
- Developer experience and time-to-production are your top priorities
- You want server-authoritative logic (validation, authorization, complex business rules on the server)
- Your app is primarily online with good offline tolerance (short disconnections, not days offline)
- You value the simplest mental model: server is truth, client is cache, mutations are validated
- React is your primary framework and you want the best React integration available
- You are building a collaborative app where conflict resolution needs to be business-logic-aware
Avoid All Three When
If your app is a simple CRUD tool with a single user and no offline needs, local-first sync is over-engineering. Use TanStack Query (or SWR, or plain fetch) with a REST API. Local-first sync adds complexity that only pays off when you have concurrent users, offline requirements, or latency-sensitive interactions.
The Hybrid Approach
One pattern we have deployed successfully: use TanStack DB as the client-side data layer with ElectricSQL as the sync backend. You get TanStack's excellent React bindings and ecosystem integration combined with ElectricSQL's robust Postgres sync. Zero does not plug into this pattern as easily because it provides its own client-side query layer. But TanStack DB plus ElectricSQL is a genuinely compelling stack for Postgres-centric teams.
The local-first ecosystem is moving fast. By 2027, we expect better interoperability between these tools, standardized sync protocols, and more mature mobile support across the board. For now, choose based on your team's strengths and your product's actual requirements, not hypothetical future needs.
If you are evaluating local-first sync for your product and want to avoid the costly mistakes we have seen teams make, book a free strategy call with our engineering team. We will help you map the tradeoffs and pick the right architecture for your specific use case.
Need help building this?
Our team has launched 50+ products for startups and ambitious brands. Let's talk about your project.