Skip to main content
Dawloom

Offline-first React Native database: PowerSync vs WatermelonDB

Dawloom engineering 10 min read
Offline-first React Native database: PowerSync vs WatermelonDB

For most offline-first React Native apps backed by Postgres, MySQL, SQL Server, or MongoDB, PowerSync is the safest pick in late 2026: it keeps a local SQLite copy in sync for you and sends local writes to your own API. Pick WatermelonDB if you already have a backend team that wants to own the sync endpoints, Electric if your data is mostly read and you’re on Postgres, and a custom queue only if the app has a handful of tables and one user per record.

The harder choice is the conflict rule, not the library. All of these hand the final say on a conflicting write to your server, so you decide what happens when two phones change the same row offline. The video below shows that one moment.

Who this is for: teams building a field, retail, delivery, or inspection app where people keep working with no signal. Who should skip it: if the app only needs to show cached data when offline, a query cache with persistence (for example TanStack Query) is enough. You don’t need a sync engine.

The conflict, in 55 seconds

Transcript (the video has no sound; this is every line on screen)

0:00. Two phones. One order. No signal. Who wins when they both come back online?

0:05. 1. Offline: both edit the same order. Ana and Ben both have order 1042 open. Qty is 2 and the note is empty. Ana changes qty to 3 at 10:02. Ben changes qty to 5 and adds the note “Leave at door” at 10:04. Each phone writes to its own SQLite file and keeps a queue of changes. The server still shows qty 2.

0:16. 2. Back online: the upload that arrives last wins each field. Ben reconnects first. His upload sets qty 5 and the note on the server. Ana reconnects second. Her upload sets qty 3. With per-field last write wins, Ben’s note survives but his qty 5 is lost. Ana’s older edit wins because it arrived later. Both phones then show qty 3 and “Leave at door”.

0:29. 3. What each library does by default. PowerSync: phones upload their queue to your backend API, your code writes to the database, and the simplest backend gives per-field last write wins, with deletes winning. WatermelonDB: the server must reject a push if the row changed since the phone’s last pull; the phone pulls, keeps its own changed columns, and pushes again. ElectricSQL: syncs reads only, writes go through your own API, and you decide what happens to local changes when a write is rejected. All three hand the final say to your server. None of them knows that qty is a stock count.

0:41. 4. So what do you do? Pick the rule for each field first. A note or a title: last write wins is fine. A stock count: send “add 1”, not the new total. A status like Delivered: a server rule that rejects edits after delivery. Then choose the library that lets your server apply those rules.

0:51. Choose the conflict rule first. Then the library. dawloom.com

The video is an animation of the documented default behavior, not a recording of the libraries running. Every rule in it is quoted with its source below.

The decision table

All facts checked 2026-10-13 on each project’s docs, pricing page, GitHub, and npm.

The decision table
PowerSyncWatermelonDBElectricCustom queue
What it isSync service plus client SDKsLocal database with sync primitivesRead-path sync engine for Postgres, plus TanStack DB on the clientYour own SQLite tables, change log, and API
Server databasesPostgres, MongoDB, MySQL, SQL Server, ConvexAny. You write the two endpointsPostgresAny
WritesUpload queue to your APIpushChanges to your APIYour APIYour API
Default conflict rulePer-field last write wins in the simplest backendServer rejects stale pushes; client’s changed columns winNone built in. You chooseWhatever you build
React Native / ExpoYes, OP-SQLite; Expo Go needs a JS adapterYes, needs native codeYes, TanStack DB with expo-sqlite persistenceYes
Other clientsFlutter, Kotlin, Swift, web, .NET (beta)WebWeb, many JS runtimesAny
LicenseClient Apache-2.0; service FSL-1.1-ALv2MITApache-2.0Yours
Hosted priceFree, Pro from $49/mo, Team from $599/moNone. You hostPay as you go from $0, Pro $249/moYour servers
Latest release@powersync/react-native 2.3.1, 2026-10-01@nozbe/watermelondb 0.28.0, 2025-04-07@electric-sql/client 1.5.28, 2026-09-09n/a
Pick it whenYou want sync done for you on a SQL or Mongo backendYou want to own every line of the sync protocolYour app is read-heavy and on PostgresA few tables, one owner per record

Release dates come from npm. Sources for every other cell are in the sections below.

PowerSync

PowerSync replicates from your server database into a SQLite file on each device. Supported sources are “Postgres, MongoDB, MySQL, SQL Server or Convex” (PowerSync docs). Writes don’t go through PowerSync. They go into a local queue that “must be sequentially drained against backend API endpoints configured by the developer” (handling update conflicts).

On conflicts, PowerSync says it is “unopinionated when it comes to handling conflicts on the backend”, and adds: “In the simplest backend implementation, the behavior of the overall system will be per-field Last-Write-Wins (LWW).” Its recommended defaults are that deletes win over later updates, and that concurrent updates resolve field by field, last to reach the server wins. That is the outcome in the video.

On React Native, the SDK uses OP-SQLite. “Our native database adapter is not compatible with Expo Go’s sandbox environment”, so Expo Go needs the JavaScript adapter @powersync/adapter-sql-js (React Native and Expo SDK). A development build works with the native adapter.

Pricing on 2026-10-13 (PowerSync pricing): Free at “$0/month” with 2 GB synced and 50 peak concurrent clients, Pro “From $49/month” with 30 GB synced and 1,000 clients, Team “From $599/month”. You can self-host the “free source-available edition”. The service’s license is FSL-1.1-ALv2, which is source-available rather than open source until it converts to Apache 2.0 (GitHub).

Watch out for: the free tier deactivates inactive projects. PowerSync’s page says “1 week of inactivity” in one place and “longer than 2 weeks” in another.

WatermelonDB

WatermelonDB is a fast local database for React Native, with sync you wire up yourself. The docs say “Watermelon is only a local database” and “you need to bring your own backend” (sync intro). You write two endpoints: changes come down in pullChanges and go up in pushChanges.

The protocol puts conflict detection on the server. If a record “has been modified on the server after lastPulledAt, you MUST abort push and return an error code” (backend docs). The client then pulls again and merges. The default merge is in the source: “This is a per-column resolution algorithm. All columns that were changed locally win and will be applied on top of the remote version” (helpers.js). You can override it with a conflictResolver function (frontend docs).

In the video’s case, Ana’s push is rejected because Ben’s change landed first. Her phone pulls, keeps its own changed column (qty 3), and pushes again. Same outcome as PowerSync, reached a different way.

Watch out for: the pace of releases. The last stable npm release is 0.28.0 from 2025-04-07, with a 0.28.1 pre-release on 2025-07-24, and the last commit on the main branch was 2025-08-11 (npm and GitHub, checked 2026-10-13). The repo has 11,792 stars and an MIT license, and the code works, but check that it supports your React Native version before you commit.

Electric

Electric today is a sync engine for reads. Its docs are direct: “Electric does read-path sync” and “Electric does not do write-path sync” (writes guide). You send writes through your own API and pick one of four patterns, from plain online writes to a local through-the-database queue.

Conflicts are yours to handle. The guide names the two hard parts as merge logic and rollbacks, and says a simple strategy is “to clear all local state if any write is rejected.” It also quotes the local-first experience that “conflicts are extremely rare and can be mitigated well by strategies like presence.”

For Expo, “You can use the Electric Typescript client in your Expo applications” (Expo integration), but “PGlite doesn’t yet work in React Native.” For an offline store on the phone, the current path is TanStack DB with @tanstack/expo-db-sqlite-persistence, version 0.2.33 on npm. TanStack DB itself is at 0.13.0, so it’s still before 1.0.

Pricing (Electric pricing, checked 2026-10-13): “Pay for writes and retention. Reads are free”. Pay as you go is $0 a month plus $1 per million writes, “Under $5/mo waived”. Pro is $249 a month. The engine is Apache-2.0 on GitHub.

A custom queue

You can build the same pattern yourself: a SQLite table per entity, a change log table, a background job that sends changes to your API, and a pull endpoint that returns rows changed since a timestamp. That’s what WatermelonDB’s protocol writes down, so its backend docs are a good spec even if you don’t use the library.

Build it yourself when there are only a few tables and each record has one owner, so conflicts can’t happen. Once two people can edit the same record offline, you’re rebuilding PowerSync’s upload queue and WatermelonDB’s conflict rules yourself, plus the tests for them.

If you were on Realm

MongoDB’s Atlas Device Sync, the sync behind Realm, is gone. MongoDB’s own page says: “Device Sync has reached its end-of-life status and be removed on September 30, 2025” (MongoDB docs). If your app still ships Realm with sync, its data has nowhere to go. PowerSync supports MongoDB as a source and publishes a migration guide from Atlas Device Sync.

Pick the rule for each field first

All three tools give your server the last word. So before you choose one, write down the rule for every field two people might edit offline:

Pick the rule for each field first
Kind of fieldRuleHow
Notes, titles, descriptionsLast write wins is fineDefault in PowerSync’s simplest backend and WatermelonDB’s merge
Counts, stock, balancesNever send the new totalSend an operation such as “add 1” and let the server add it up
Status that ends a flow (Delivered, Paid)Server ruleReject edits after the final status, and tell the user
Long text two people write togetherMergeA CRDT such as Yjs, which PowerSync’s docs mention as an option

If two fields need a rule like the second or third row, choose a tool where your own API applies writes. That’s all four options here, which is why the rule matters more than the brand.

FAQ

What is the best offline-first database for React Native?

For local storage alone, SQLite. Expo’s expo-sqlite and OP-SQLite are the common choices. For storage plus sync to a server, PowerSync has the widest backend support and the most active releases as of October 2026. WatermelonDB is a strong local database if you write the sync endpoints yourself.

Does WatermelonDB still get updates?

Slowly. The last stable release on npm is 0.28.0 from April 2025 and the last commit on the main branch was August 2025, as of 2026-10-13. It still works, but check it supports your React Native version first.

Can Electric sync writes from a phone?

No. Electric only syncs reads. You send writes to your own API, and Electric syncs the result back down. TanStack DB can hold the optimistic local writes until then.

How does PowerSync handle conflicts?

Your backend decides. PowerSync uploads each local change to your API in order. If your API simply writes each change, the result is per-field last write wins, with deletes winning over later edits.

What replaced Realm sync?

MongoDB removed Atlas Device Sync on September 30, 2025. Realm still works as a local database. For sync, teams have moved to PowerSync, or their own API. MongoDB’s page lists other providers too.

We build offline-first mobile apps for retail and field teams, including the sync rules. See mobile app development and our notes on offline-first selling. For the sync patterns on the server side, read keeping Shopify in sync with an inventory system, background jobs that don’t fall over, and why Postgres is our default database. If your app runs on Expo, Expo or bare React Native covers when you need a development build, which the native SQLite adapters require.

More in Mobile apps Need this built? See Mobile app development

Got something to build?

Tell us what you need. An engineer replies, not a sales team.

Search the whole site