My Journey to Pick One Codebase for All Devices

React Native vs Tauri vs Capacitor (And What I Actually Chose) in 2025 July

I’m building a consumer SaaS app at the intersection of AI and therapy. It’s deeply personal. I care about the space. I care about the experience.

From the start, I knew two things:

  • I want the product to feel mobile-first
  • But I also want it to work great on desktop

That decision sounds simple, but it made everything more complex.


My product needs

Here’s what I knew I needed from the tech stack:

  • Runs on iOS, Android, and web
  • Works offline
  • Syncs data across devices
  • Fast to iterate
  • Simple layouting for both desktop (sidebar) and mobile (drawer)
  • Text-heavy UI with journaling + habit loops
  • Built with React and Tailwind (if possible)

The options I explored

🌀 React Native (with Expo)

  • ✅ Battle-tested
  • ✅ Easy mobile preview (Thank you expo!)
  • ❌ No Tailwind
  • ❌ Can’t reuse most web code
  • ⚠️ My React Native skills are rusty

🧪 Tauri

  • ✅ Uses Vite
  • ✅ Works with Tailwind, ShadCN
  • ❌ Painful iOS builds
  • ❌ Smaller community
  • ⚠️ Mobile support is quite early

🧩 Capacitor + React + Vite

  • ✅ Uses standard React stack
  • ✅ Works with Tailwind + ShadCN
  • ✅ Wraps as native shell for mobile
  • ✅ Shared codebase for web + mobile
  • ⚠️ Some quirks with keyboard + focus management

→ I chose this one.


Handling Sync: PowerSync

One of the hardest parts of this app was sync.

People journal from multiple devices.

They expect data to always be saved and in sync — even offline.

I didn’t want to build my own sync engine.

So I’m using PowerSync.

It gives me:

  • Offline-first sync
  • A local embedded database (SQLite)
  • Sync with Postgres
  • Great dev experience

It removed a huge chunk of infrastructure pain.


Layouting for Mobile vs Web

Layout was another tricky area.

On desktop, I use a sidebar layout for journal list, tag filters, etc.

On mobile, I switch to a drawer + bottom tab navigation.

I built a simple layout context with media queries to switch between them.

Tailwind + ShadCN made this easier.


Live updates with Capgo

To avoid full App Store resubmits for every fix, I integrated Capgo.

It lets me:

  • Push JS/CSS updates over-the-air
  • Fix bugs or update flows without triggering a new app review
  • Keep velocity high post-launch

This has been a game changer for iteration speed.


CI/CD with GitHub Actions

I set up a GitHub Action that:

  1. Runs tests + linting
  2. Builds the web app
  3. Builds mobile bundles
  4. Pushes Capgo live update artifacts
  5. Optionally triggers TestFlight or Play Console uploads

Deployment is predictable. It fits into my normal dev loop.


Final Stack (for now)

LayerTool
UI FrameworkReact + Tailwind + ShadCN
Mobile WrapperCapacitor
Native UpdatesCapgo
Sync + Offline DBPowerSync
Build ToolsVite, GitHub Actions
LayoutMedia query hooks + context
PlatformsiOS, Android, Web

Why I chose this path

  • I wanted to reuse my React skillset
  • I wanted to ship on all platforms without rewriting
  • I didn’t want to compromise on layout or UX
  • I needed offline sync to just work

Capacitor + PowerSync + Capgo gave me the flexibility I wanted, with fewer surprises than React Native or Tauri.

Still early days. But I’m shipping. And that’s what matters.