Skip to content

Nizhal vs Zero, LiveStore, Electric, PowerSync

Copy page

These tools all solve a version of “keep a local store in sync with a backend so the app works offline and updates live.” They differ on five axes that actually matter:

  1. Source of truth / write model — is the device authoritative, or the server?
  2. CDC mechanism — how do server-side changes get detected? (log-based replication vs triggers vs an event log)
  3. What syncs — rows, query results, events, or shapes?
  4. Deployment — does it need an always-on stateful service, or does it run on plain serverless?
  5. Realtime — is the live connection foundational, or an optional latency optimization?

We’ve tried to be fair — every tool here is good, and each is better than Nizhal for some use case. Claims are grounded in each project’s own docs (linked at the bottom); versions: Zero 1.0, LiveStore 0.4, PowerSync 1.x, Electric current.

NizhalZero (Rocicorp)LiveStoreElectricPowerSync
Source of truthdevice or server (per-collection mode)server (Postgres)the event log (local-first)server (Postgres)server DB
Writesoptimistic; durable outbox; local-first or server-authoritativeoptimistic, server-authoritative (rebase)event-sourced, rebaseread-path only — you build writesclient upload queue → your backend
CDC sourcetriggers (Postgres and libSQL/SQLite, no WAL)Postgres logical replicationthe event log itselfPostgres logical replicationlogical replication (Postgres) / change streams
Unit of syncrows in buckets + mutation commandsquery results (ZQL, dynamic)events (eventlog)Shapes (live row sets)rows by Sync Rules
Backend DBsPostgres, libSQL/TursoPostgres onlyany (events stored by a sync backend)Postgres onlyPostgres, MongoDB, MySQL
Needs always-on stateful service?No (HTTP runs serverless)Yes (zero-cache + replication slot)a sync backend (pluggable, CF-able)a sync service (replication consumer)their sync service
Realtimeyes, but optional (pull-interval backstop)yes (streaming, core)yes (notify→pull events)yes (streaming shapes)yes (live)
ConflictLWW / field-level / CRDT, per moderebase, server winsLWW (+ custom merge)optimistic, reconcile by txidLWW-ish; you own write logic
Maturityyoung1.0, mature0.4, activematuremature, commercial

Zero (Rocicorp) — a zero-client + a stateful zero-cache that keeps a read-only replica of your Postgres via logical replication. You write normal queries (ZQL) and Zero syncs exactly the data those queries need — dynamic, no static sync rules. Writes are optimistic + server-authoritative. Best for: Postgres apps that want “just write live queries,” and can run an always-on zero-cache. Weakness: Postgres-only; that stateful cache isn’t serverless.

LiveStoreevent sourcing: every mutation is an immutable event; local reactive SQLite is a projection of the eventlog; sync is git-style pull-then-push-with-rebase of events. Best for: apps that want auditability, time-travel, and custom merge semantics, and are happy thinking in events. Weakness: event-sourcing is a bigger mental model than rows.

Electric (ElectricSQL) — consumes the Postgres logical-replication stream and fans rows into Shapes that clients subscribe to. Deliberately read-path only: writes go through your server functions and reconcile optimistically via a Postgres transaction id. Best for: scaling read sync over Postgres with minimal lock-in; you keep full control of writes. Weakness: you build the entire write path yourself.

PowerSync — a drop-in sync layer that mirrors your backend DB into in-app SQLite via a sync service + Sync Rules, with a client-side upload queue that replays CRUD to your backend. Supports Postgres, MongoDB, and MySQL. Best for: production mobile/brownfield apps wanting a battle-tested, multi-DB, commercially-supported engine. Weakness: runs through their sync service; commercial product.

Nizhal — row-based CDC via triggers (so it works on Postgres and libSQL/SQLite with no WAL / logical replication), cursor-pull + idempotent-push of mutation commands, with realtime as an optional ping rather than the data path. Source of truth is per-collection: local-first (default) or server-authoritative. Includes a brownfield sync target to point the client at your existing API.

The two design choices that set Nizhal apart

Section titled “The two design choices that set Nizhal apart”

1. Realtime is optional, not foundational. In Zero, Electric, and PowerSync the live streaming connection is the model. In Nizhal the data path is pull (cursor delta) + push (idempotent); the WebSocket only sends a repull:<bucket> ping that says “pull now.” Lose the socket and the app still converges on the next pull interval — just slower. This is why Nizhal runs on plain serverless (HTTP everywhere; add a realtime hub only if you want instant) and degrades gracefully where the others assume an always-on connection. See Realtime.

2. Trigger-CDC instead of logical replication. Zero and Electric require Postgres logical replication (a replication slot + an always-on consumer). Nizhal detects changes with triggers, so the same engine runs on libSQL/SQLite (Turso, embedded) and on Postgres without WAL config — at the cost of not getting replication’s zero-app-overhead change capture.

  • Pick Zero if you’re all-in on Postgres, want dynamic query-driven sync with the least sync-rule ceremony, and can run a stateful zero-cache. Revisit Nizhal if you need libSQL/SQLite, serverless deployment, or realtime-optional degradation.
  • Pick LiveStore if you want event-sourcing’s auditability/time-travel and custom merges. Revisit Nizhal if you’d rather sync rows than model everything as events.
  • Pick Electric if you only need fast read sync over Postgres and will own the write path. Revisit Nizhal if you want writes, offline durability, and conflict handling built in.
  • Pick PowerSync if you need a mature, commercially-supported engine across Postgres/Mongo/MySQL, especially on mobile. Revisit Nizhal if you want to self-host with no dedicated sync service, run on libSQL, or pick source-of-truth per collection.
  • Pick Nizhal if you want: realtime that’s optional (so plain serverless works and the app survives a dropped socket), trigger-CDC that runs on Postgres and libSQL/SQLite with no WAL, per-collection local-first vs server-authoritative, and a brownfield adapter to reuse an existing backend. Revisit a competitor if you need Zero’s dynamic queries, LiveStore’s event log, or PowerSync’s multi-DB maturity today.
  • No dynamic query-driven sync (you define sync rules → buckets, like PowerSync/Electric, not ad-hoc queries like Zero).
  • No event-log/time-travel model (rows + tombstones, not an eventlog like LiveStore).
  • No MongoDB/MySQL backends (Postgres + libSQL/SQLite).
  • Younger than all four — fewer production miles.