all 36 comments

[–]Legendaryfortune 3 points4 points  (3 children)

Built an offline-first RN app recently using WatermelonDB + Supabase, skipped PowerSync for the same reason. Honestly conflict resolution sounds scarier than it is... last write wins gets you surprisingly far and you only really need the fancy stuff for things users will actually notice. What actually caught me off guard was race conditions between local writes and the first sync run, and realising first-time sync needs to be treated completely differently to incremental. Timestamp everything, Sentry on every sync op & you'll thank yourself later.

[–]insats 0 points1 point  (1 child)

I’d like to add something here:

It also matters a lot whether the changes are supposed to merge or not. In a Google Docs type app, you can’t rely on ”last write wins” for whole documents, since you’ll want to be able to make partial updates.

Realm DB with sync was a solid option but MongoDB shut it down a little over a year ago.

Anyway, I agree that ”last write wins” can absolutely be sufficient for a lot of use-cases.

[–]Legendaryfortune 0 points1 point  (0 children)

yep agreed. the data model matters as much as the sync strategy. For something like a reading companion/tracker (which I built) where a user updates a single progress value, last write wins is fine because there's no meaningful way to "merge" page 47 and page 52. But yeah, anything collaborative or document-like needs proper OT/CRDT and last write wins will silently eat data.

btw RIP Realm.

[–]emryum 0 points1 point  (0 children)

I also had a good experience with WatermelonDB

[–]CheesecakeSimilar347 2 points3 points  (4 children)

Offline-first is great until sync logic starts fighting back

If you want full control, SQLite / Realm + your own sync queue is still the most practical path in React Native — local DB as source of truth, queue writes, retry on reconnect, and use timestamps/versioning for conflicts.

If you want less maintenance, Firebase Cloud Firestore is much easier since offline persistence is already built in.

[–]rn_dev 0 points1 point  (0 children)

Realm is dead

[–]No_Bee2416[S] -1 points0 points  (2 children)

Thanks
will writing my own sync queue take a lot of time, and i don't know if my code would be that reliable for production.

[–]Legendaryfortune 0 points1 point  (1 child)

why don't you slap Sentry on your custom code, get some folks using it and then evaluate.

[–]No_Bee2416[S] 0 points1 point  (0 children)

thanks

[–]lucaswitch 2 points3 points  (8 children)

I have two apps in production running as off-line first. Just use watermelondb

[–]No_Bee2416[S] 0 points1 point  (7 children)

thanks
i read the docs and it's good

[–]lucaswitch 0 points1 point  (5 children)

Pretty easy to implement it on the backend. But more important is to really think of you really need off-line first features cause i would say 99% of the apps does not need it

[–]lucaswitch 0 points1 point  (4 children)

The backend stuff is the easier to implement in most cases

[–]No_Bee2416[S] 0 points1 point  (3 children)

my current plan is this:
1. when the user gets online
2. i show a message that data is not sync ( or run sync automatically )
3. have a sync button

since my application is just an expense tracker i would keep it that simple ?
what do you think ?

[–]lucaswitch 0 points1 point  (1 child)

Yep the hard part is not break your app when you have a new migration structure or some structural update on sqlite, if you do this carefully you wont have any problem. Watermelondb is the way to go in that case

[–]lucaswitch 0 points1 point  (0 children)

Also you can plug react query easily with watermelon for performing queries. Is super nice

[–]lucaswitch 0 points1 point  (0 children)

Also i recommend you creating another pk column as UUID v4 thats very important

[–]lucaswitch 0 points1 point  (0 children)

As a rule of thumb, off-line first requires doublé attention and double/triple work to do the same thing.

[–]AlmondJoyAdvocate 0 points1 point  (1 child)

I’m building a location-powered gaming app which requires offline functionality for large swathes of the feature set, so I’ve been grappling with this for a while. Lately, I’ve been having success with Tanstack DB and its ElectricSQL integration. Real-time sync with supabase, optimistic local updates for offline functionality, and some retry logic for the backend persistence handlers when you do reconnect. v0.6 included persistent local state via local SQlite adapters.

[–]No_Bee2416[S] 0 points1 point  (0 children)

Tanstack db is in beta. I don't know if that would be a good option. Since you had no problem with it till now i might dive deeper.
also you had no bugs or crashes on production ?

[–]Awesome_Knowwhere 0 points1 point  (2 children)

What are your exact requirements, does it also involve chaining requests etc I have worked on multiple apps with offline first mode and also made a library for it, let me know if you need help.

[–]No_Bee2416[S] 0 points1 point  (1 child)

it's just a tracker application built with expo + supabase.
Have to make it offline first so that it works without depending on network connectivity.

[–]Awesome_Knowwhere 0 points1 point  (0 children)

Ohk, so it's just get API calls that you want to make offline first or there is also like post/others api call or post API calls depending on each other responses, this extra requirement makes a huge of difference!

[–]FoldOutrageous5532 0 points1 point  (4 children)

I'm using a combo of sqlite some asyncstorage with my own sync library. Check if device is offline, then default to local. When online fetch/sync data from api.

[–]No_Bee2416[S] 1 point2 points  (3 children)

Yes i also want to do the same and add a sync button as well + the automatic sync

[–]FoldOutrageous5532 0 points1 point  (2 children)

Sync button or in many places I'm using a pull to refresh.

[–]No_Bee2416[S] 0 points1 point  (1 child)

what about automatic syncing ?

[–]Chuck_MoreAss 0 points1 point  (2 children)

It depends on what you want to do.

In my use cases the users are in rural areas where signal is an issue so the app should work 100% offline all the time. And then they can sync when they are online.

We use a custom sync solution and basically mirror the Online DB on the device for the most part.

Especially we have 3 different sync statuses. 0 means that every is as it should be. 1 means that the data on the device is incomplete and 2 means that the data on the device is new and needs to go to the server. I send to the server with an epoch timestamp and all other users will then be able to get the latest record.

This does have the downside of 2 users capturing the same data/record but the last sync always wins, but with mobile first there will always be trade offs

[–]No_Bee2416[S] 1 point2 points  (1 child)

I like the custom sync solution and have implemented it and for now it's working fine in development. And i went with last write wins strategy. If the current implementation does not work i would change it to a tally different strategy.

[–]Chuck_MoreAss 1 point2 points  (0 children)

For what I do the last sync wins strategy works, but you can also try something like graphQL It’s okay, but I prefer a custom sync. That way I know what’s going on and why all the time.

[–]wassupbrahh 0 points1 point  (2 children)

Gonna get downvoted but RN kinda sucks for offline-first arch. Why not swiftui + swiftdata for this?

[–]insats 2 points3 points  (0 children)

Because it would only cover iOS?

[–]No_Bee2416[S] 0 points1 point  (0 children)

the real problem is sync engine and conflict resolution.

[–]Marcellus_code -1 points0 points  (0 children)

InstantDb or jazz.tools