THE DECISION THAT SHAPES EVERYTHING
When we started building Tasted — a personal drinks journal — the first product question was also the first architectural question: does the user need an account?
The honest answer was no. A drinks journal is personal data. The user knows what they drank. Nobody else needs to see it. Requiring an account to use a logging app is friction that serves the developer (easier sync, easier analytics, easier push notifications) not the user.
So we decided: no account required. Everything lives on the device. Offline first, local first, always yours.
That decision felt simple. The implementation was not.
WHAT "OFFLINE-FIRST" ACTUALLY MEANS
Most apps are online-first with offline tolerance — they call an API, cache the response, and show a spinner when there's no connection. Offline-first inverts this: the local database is the source of truth, and the network (if it exists at all) is secondary.
For Tasted, this meant:
- All reads come from the local SQLite database, never from an API
- All writes go to the local database immediately, with no waiting for network confirmation
- The UI never shows a loading state caused by network latency
- The app is fully functional on a plane, in a cellar, in a restaurant with no signal
The benefit to the user is immediate and tangible: the app is fast, always available, and doesn't require them to trust a server they didn't ask for.
THE STORAGE LAYER
We used SQLite via expo-sqlite — a well-supported, mature option for React Native that gives you a real relational database on device. We considered WatermelonDB (a higher-level ORM built on SQLite) and opted against it; the abstraction cost wasn't worth it for a data model as straightforward as ours.
Schema migrations are the first thing that bites you. On web, you can migrate your database and redeploy in minutes. On mobile, you're migrating a database that lives on devices you don't control, across app store update cycles that might span weeks. We implemented a version-tracked migration system from day one — each migration is numbered, stored in the app bundle, and applied in sequence on startup if the user's database is behind.
Skipping this setup early is the single most common mistake we see in mobile app codebases. It's painful to retrofit.
THE SYNC QUESTION
Offline-first doesn't mean no sync. It means local-first sync — the device owns the data, and sync is additive, not authoritative.
For Tasted's optional iCloud backup feature, we implemented a last-write-wins strategy with client-side timestamps. When a user restores from backup or logs in on a second device, newer records win. It's not perfect — if someone logs the same wine simultaneously on two devices with no network, one entry wins and one is discarded — but for a personal journal, this edge case is acceptable.
More complex apps need CRDT-based sync (Conflict-free Replicated Data Types) where every write is composable and order-independent. We've used this for collaborative tools. It's significantly more complex to implement and should be reserved for cases where simultaneous multi-device edits are genuinely expected.
The rule of thumb: for personal data, last-write-wins is usually enough. For collaborative data, reach for CRDTs or operational transforms.
WHAT NOBODY WARNS YOU ABOUT
A few things that weren't obvious until we were deep in:
- Testing is harder. You can't inspect the device database in production the way you can query a server. Build your debug tooling early — we added a hidden developer screen that shows raw database contents and lets us export the SQLite file.
- App size grows. Bundling SQLite adds weight. Not a dealbreaker, but worth tracking from the start rather than discovering it at submission time.
- The AI features still need a server. Tasted's AI-powered tasting notes call an LLM API. We implemented a graceful fallback — if there's no network, the user gets a simpler set of preset descriptors instead. The core app never breaks; the AI feature just degrades.
- Privacy becomes a selling point. "Your data never leaves your device" turned out to be a meaningful differentiator. Users in the App Store reviews cite it explicitly. The architectural decision and the product narrative ended up being the same thing.
THE VERDICT
Offline-first architecture is more work upfront and pays dividends forever. Faster UI, no server costs for storage, better user trust, and a product that works in the real world — where connections are unreliable and users don't want to hand over their data to use a journaling app.
If your app's data is fundamentally personal — a journal, a tracker, a local tool — start with offline-first and build sync on top if you ever need it. It's much harder to go the other direction.