Cross-platform Mobile — Comparing the Candidates
The desire to build the same app for iOS, Android, and the web from a single codebase has been around for a long time. The tools that emerged as answers picked different approaches in different eras. Some sit on top of…
Table of contents
The desire to build the same app for iOS, Android, and the web from a single codebase has been around for a long time. The tools that emerged as answers picked different approaches in different eras. Some sit on top of a WebView, some call native widgets through a JavaScript bridge, and some draw every pixel onto their own canvas. This post walks through the timeline from PhoneGap to Tauri Mobile and the trade-offs of each candidate.
1. Timeline
| Tool | First appeared | Model |
|---|---|---|
| PhoneGap | 2009 (Nitobi) | WebView + native plugins |
| Apache Cordova | 2011 (PhoneGap donated to ASF) | WebView + plugins |
| Ionic | 2013 | Cordova / Capacitor + Angular / React / Vue UI |
| React Native | 2015 (Meta) | JS bridge + native widgets |
| NativeScript | 2014 | JS + direct native access |
| Xamarin | 2011 (acquired by Microsoft 2016) | C# + Mono. EoL in 2024 |
| Flutter | 2017 (Google) | Dart + own canvas (Skia → Impeller) |
| Capacitor | 2019 (Ionic) | WebView successor with a cleaner plugin model |
| Expo | 2015 → workflow on top of React Native | Managed + EAS Build |
| .NET MAUI | 2022 (Xamarin successor) | C# + platform handlers |
| Kotlin Multiplatform | 2018 alpha · 2023 stable | Shared Kotlin code, UI via Compose Multiplatform or native |
| Tauri Mobile | 2024 alpha | WebView + Rust core. Mobile extension of desktop Tauri |
2. Three models
Sit on a WebView — embed the browser engine inside the app and draw the web app on top of it. iOS WKWebView and Android WebView are the stages.
- Cordova / Capacitor — web app + native plugin bridge.
- Ionic — Capacitor + UI kit.
- Tauri Mobile — WebView + Rust core for OS calls.
Strengths — web technologies (HTML, CSS, JS) work as-is. Bundles stay relatively small. Easy to share the same code with desktop and the web. Limits — small differences from native widgets in look and behaviour. Heavy animations and complex gestures can lag. iOS WebView policies and limited push notification support.
JavaScript bridge + native widgets — code written in a JS runtime composes native UI widgets.
- React Native — JS → native components. The new architecture (Fabric, TurboModules, JSI) reduces bridge overhead.
- NativeScript — JS calls native APIs directly.
Strengths — native UI look and feel. The React ecosystem (RN). Limits — platform-specific differences still end up as native code branches. Build environments (Xcode, Android SDK) are still required. Library compatibility.
Render directly onto an own canvas — instead of using platform widgets, draw every pixel through a custom graphics engine.
- Flutter — direct rendering with Skia → Impeller. Material and Cupertino widgets are also drawn internally.
Strengths — pixel-level consistency. 60 / 120 fps animations feel natural. Smooth hot reload. Limits — when the platform introduces new widgets, the framework has to follow. Accessibility and platform integration (system share sheets) need separate implementations. Bundle size is larger than other models.
3. Matrix
| Tool | Language | Rendering | Strengths | Limits |
|---|---|---|---|---|
| React Native | JS / TS | Native widgets | React ecosystem · familiar UI | Native module compatibility issues |
| Flutter | Dart | Own canvas | Consistency · performance | Large bundle · learning Dart |
| Capacitor / Ionic | JS / TS | WebView | Shares web code | WebView limits |
| Expo | JS / TS | On top of RN | Build · deploy automation | Managed constraints (eased by EAS) |
| Tauri Mobile | JS / TS + Rust | WebView | Small bundle · Rust safety | Mobile is alpha |
| Kotlin Multiplatform | Kotlin | Native or Compose | Code sharing + native UI option | Sharing UI needs extra tooling |
| .NET MAUI | C# | Native handlers | C# ecosystem · Visual Studio | Smaller community |
| Native (Swift · Kotlin) | Swift · Kotlin | Native | Best integration · accessibility | Two codebases |
4. Decision factors
Which experience for which user:
- Are the small look-and-feel differences between the two OSes part of the business value (native or RN).
- Do consistent pixels matter (Flutter).
- Want to share code with the web (WebView family).
Team's tech stack:
- React team — RN.
- Vue · Angular — Capacitor + Ionic.
- C# — MAUI.
- Kotlin / Java — KMP or native.
- Rust-friendly — Tauri Mobile (alpha, though).
Build · deploy — managed services (Expo EAS, Codemagic, Bitrise) cut down the burden of running build environments. Running native Xcode / Android Studio directly gives more freedom but more infra cost.
Library availability — check the maturity of libraries for core modules like in-app purchases (IAP), push, camera, and Bluetooth.
Accessibility · i18n — own-canvas tools need a separate mapping to the OS accessibility tree. Native widgets are usually stronger here.
5. Operations
iOS build requires macOS — building an iOS app needs Xcode, which only runs on macOS. Windows and Linux developers have options:
- macOS virtual machine or a Mac mini build server.
- Cloud build services (EAS, Codemagic, Bitrise, GitHub Actions macOS runner).
Android builds work on Windows, Mac, and Linux alike.
Code signing:
- iOS — Apple Developer account + certificate + provisioning profile.
- Android — keystore + Play App Signing.
For details see 03-android-build-apk.
OTA (Over-The-Air) updates — a model that ships JavaScript-only changes to user devices. The scope allowed under App Store and Play Store policies:
- Expo Updates / EAS Update.
- CodePush (Microsoft, sunset announced in 2024 → migration needed).
- expo-updates · self-hosted.
Platform policies allow business-logic tweaks but treat changes to the app's essential function as subject to store review.
6. Common pitfalls
Payments and subscriptions — IAP fee policies of the iOS App Store and Google Play shape the business model.
App store review — common rejection reasons on first submission are privacy policy, test accounts, and feature clarity.
Permission model — iOS and Android differ in permission timing and wording. The flow after a user denial differs too.
Background work — both OSes have aggressive battery policies, so periodic jobs and location often don't run as expected.
Push notifications — iOS background data messages have reliability limits.
Version fragmentation — iOS has fast adoption, Android has wide OS-version diversity. minSdk choice matters.
OS-update compatibility breaks — every year Apple and Google ship new SDKs and some libraries break. Check library maintenance activity.
Localization · regulation — GDPR, personal-data laws, children's data policies (COPPA). Review before a global launch.
Closing thoughts
Cross-platform tooling has different answers for different eras. The core decision is choosing among the WebView, JS-bridge, and own-canvas trade-offs to fit the environment. Team stack + build infra + library availability are the actual weights on the scale. Native is no longer the absolute answer, but it remains the right call where small differences carry business value.
Next
- flutter-basics
- android-build-apk
React Native official · Flutter official · Capacitor official · Tauri official · Kotlin Multiplatform · .NET MAUI official · Apple Developer App Store Review · Google Play Policy Center for reference.
More in mobile
All in this category →Related posts
Windows vs macOS dev environment
Even with the same language and the same code, the basics like paths, line endings, shells, and permissions shift once the operating system changes. Linux falls on the macOS side of this split (both are Unix-flavored).
Cross-platform scripts
When a project targets users on Windows, macOS, and Linux all at once, the choice of tool for automation scripts becomes a recurring question. There is no single right answer — environment constraints and team familiari…
Flutter Basics
Flutter is a UI toolkit from Google that targets mobile, web, and desktop from a single codebase. Its own rendering engine, the Dart language, and the widget tree as an expression model are the core. This post covers Fl…