Expo vs Native (SwiftUI / Jetpack Compose): Choosing Your Mobile Stack
Cross-platform or native? We ship the same app as an Expo template, a SwiftUI template, and a Kotlin template — here's how to decide.
"Should we go cross-platform or native?" is the first — and most consequential — decision in any mobile project. It's also drowning in tribal answers.
We have a useful vantage point: SoarStarter ships the same app three ways — an Expo template (React Native, one codebase for iOS and Android), a SwiftUI template (native iOS), and a Kotlin template (native Android with Jetpack Compose). All three implement identical features on an identical backend: Supabase auth with Sign in with Apple and Google, RevenueCat subscriptions and paywalls, push notifications, onboarding, settings, and Sentry crash reporting. The comparison below is apples-to-apples because we've built the apples.
What Expo gets you
One codebase, two platforms. This is the headline and it's real. Every feature in our Expo template ships to iOS and Android simultaneously. For a small team, that's not a 2x saving — it's closer to the difference between shipping on both platforms and quietly abandoning one.
Over-the-air updates. With EAS Update, JavaScript-level changes reach users without an app-store review cycle. For fixing a copy bug or a broken layout at 11pm, OTA is the feature you didn't know you needed. (Native code changes still require a store release.)
Your web team already knows it. React, TypeScript, familiar state management. If your developers ship React on the web, they're productive in Expo in days, not months. Our Expo template will look immediately familiar to anyone who's used our web templates.
The modern Expo toolchain is genuinely good. Expo Router gives file-based, typed navigation; EAS handles builds and store submission in CI; dev clients mean you rarely touch Xcode or Android Studio directly.
What Expo costs you
A translation layer to the platform. Most native capabilities have solid Expo modules — our template uses them for notifications, secure storage, biometrics, haptics, and more. But when you need something at the platform's cutting edge (a new iOS widget type, Live Activities, App Intents, some vendor SDK), you're either waiting for a module, writing a native one, or doing without.
App size and startup. A React Native app carries its runtime. It's fine for most products, but a native app will always be leaner.
Two platforms' rules, one codebase. You still need to understand both stores' review policies, both permission models, and both design languages — cross-platform reduces code, not platform knowledge.
What native gets you
The whole platform, immediately. Our SwiftUI template speaks to iOS with nothing in between: Swift concurrency, native navigation, StoreKit, and every new API the day Apple ships it. The Kotlin template is the same story on Android — Jetpack Compose, Material 3, Hilt for dependency injection, first-class access to everything in the Android SDK.
Platform feel. Native apps inherit correct behavior by default: scroll physics, text rendering, accessibility, context menus, keyboard handling. Cross-platform frameworks approximate this well, but "well" is doing some work in that sentence, and discerning users notice.
Performance headroom. For most CRUD-and-content apps this doesn't matter. For anything graphics-heavy, latency-sensitive, or battery-critical, it does.
What native costs you
Two codebases, two teams (or one team context-switching), two review pipelines, and every feature built twice. No OTA updates — every fix rides an app-store review. And hiring is split: Swift developers can't ship your Android features, and vice versa.
A decision framework
Ask these in order:
- Do you need both platforms at launch with a small team? Expo, almost regardless of the other answers.
- Is your product deeply tied to platform capabilities — widgets, watch apps, advanced camera work, platform-specific integrations? Native.
- Does your team already write React? That's a strong thumb on the scale for Expo.
- Is the app itself the product (polish is the differentiator) or the access point (the service is the product)? Product → lean native. Access point → lean Expo.
- Only shipping on one platform anyway? Then cross-platform buys you little — go native and enjoy the depth.
The part that doesn't change
Here's what building all three taught us: the framework choice affects maybe half your codebase. The other half — auth flows, session handling, subscription entitlements, paywall logic, push registration, settings, error tracking — is product plumbing that's conceptually identical everywhere. That's why all three SoarStarter mobile templates standardize on Supabase and RevenueCat: whichever stack you pick, the backend and monetization model are the same, and switching (or adding a second stack later) doesn't mean rebuilding your business logic's foundations.
Pick the stack that fits your team, and let the template handle the plumbing: Expo, SwiftUI, or Kotlin — each with full documentation covering setup through store submission.