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:
- Runs tests + linting
- Builds the web app
- Builds mobile bundles
- Pushes Capgo live update artifacts
- Optionally triggers TestFlight or Play Console uploads
Deployment is predictable. It fits into my normal dev loop.
Final Stack (for now)
Layer | Tool |
---|---|
UI Framework | React + Tailwind + ShadCN |
Mobile Wrapper | Capacitor |
Native Updates | Capgo |
Sync + Offline DB | PowerSync |
Build Tools | Vite, GitHub Actions |
Layout | Media query hooks + context |
Platforms | iOS, 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.