33 — Mobile Apps — One Codebase, Three Ways to Reach the Pixels
"Learn Swift and Kotlin and write everything twice" was my mobile-app plan, and it was wrong in one important way. The model that finally clicked is small enough to hold in one line: every cross-platform framework reaches iOS and Android from one codebase; they differ by HOW they turn that code into on-screen pixels, and HOW MUCH of the platform's native UI you actually get. Once I separated "the language I write" from "the rendering strategy underneath," the three big names stopped being a blur and became a clean trade-off.
Here is the whole picture — one source, three rendering strategies, one phone — before I walk through each path:
Native first: what the bar is
Before the three frameworks, the thing they're all trying to avoid: writing the app twice, once in Swift or Objective-C for iOS, once in Kotlin or Java for Android. That's "native" development — separate codebases, separate teams, separate release trains, and a perfectly authentic UI on each platform because you're literally using each platform's own widgets [1]. Nobody argues the result feels wrong; they argue about the cost of getting there. Cross-platform exists to pay that cost once instead of twice.
The trade-off every framework makes is a bet on the same axis: how close to "perfectly native" does the result have to feel, and what am I willing to give up to share code? The three below sit at three different points on that axis.
React Native: bridge to real native components
React Native is the one that most directly asks "what if a <View> just meant the platform's real UIView?" You write JavaScript and React; the framework runs your JS in its own thread and bridges each UI instruction over to the device, where it's rendered using the actual native widgets — UIView on iOS, android.view.View on Android [2]. A button isn't a drawing of a button; it's the platform's button.
That's the part that clicked for me. The same <Text> or <ScrollView> I write becomes a different real component on each platform, which is why a React Native app looks and feels native out of the box — menus swipe right, switches toggle with platform physics — without me touching either SDK (software development kit — the platform's official toolkit). The cost of that authenticity is the bridge: UI work has to cross from the JS thread to the native thread and back, which is why list scrolling or gesture-heavy animation could historically feel janky (stuttery). (The New Architecture — the Fabric renderer plus JSI, a faster direct channel that removes the async bridge — is the framework's answer to exactly that.)
The payoff is skill reuse. If I already think in React components, props, and hooks, React Native lets me bring that way of thinking to mobile and reach the native UI I couldn't otherwise afford to write. Native device features — camera, GPS, push — come through community modules or react-native-* libraries rather than raw SDK calls.
Flutter: draw your own widgets
Flutter takes the opposite bet. Instead of bridging to the platform's widgets, it ships its own rendering engine (Skia/Impeller) and draws every widget itself, straight to the canvas [3]. You write Dart, not JavaScript; the framework paints a Material (Google's design style) or Cupertino (Apple's design style) widget on iOS exactly as it does on Android, because neither OS is being asked to render anything — Flutter is.
The part I had to straighten out: "draws its own widgets" sounds like a hack until you realize it's the source of Flutter's biggest strength, pixel consistency. The same app looks identical down to the sub-pixel on every platform, because the OS isn't in the rendering loop. There's no bridge to jank against; UI runs at 60–120fps because there's nothing to cross. Hot reload (seeing changes instantly without a full rebuild) is famously fast, and the widget library is rich enough that complex layouts come together quickly.
The cost is the mirror image of React Native's strength. Because Flutter isn't using the platform's widgets, it doesn't automatically feel native — it feels like Flutter, which is usually close but not identical to the real thing. And "write Dart" is a real ask: it's a new language, a new toolchain, and a smaller talent pool than the JS/React world. If consistency and performance across platforms matter more than hewing to each OS's native idioms, Flutter wins on the merits.
Ionic: a web app in a webview
Ionic is the path that reuses the most of what a frontend developer already has. You write HTML, CSS, and JavaScript — optionally with React, Vue, or Angular on top — and Ionic wraps that web app inside a webview (a native container that's effectively a sandboxed browser) and exposes device features through bridges like Capacitor [4]. The app you ship is, at its core, a web app that happens to install from an app store and can reach the camera.
This is the path with the lowest barrier and the highest code reuse if you already have a responsive web app: a lot of the same code can run on the web, on iOS, and on Android. Ionic's component library is styled to mimic native UI, so a button looks like an iOS button on iOS and an Android button on Android — but it's still a web element in a webview, not the real native control.
The trade-off is the one web developers already know is coming. A webview is a browser, with a browser's startup cost, memory footprint, and rendering characteristics. For content-driven apps — forms, lists, dashboards — the gap is small and Ionic is genuinely a fast way to ship to three platforms. For anything animation-heavy or latency-sensitive, the webview shows through, and React Native or Flutter will feel noticeably better.
How they line up
Same axis, three points:
- React Native — JS/React; bridges to real native components; authentic look/feel; shared React skills; the bridge is the historical bottleneck.
- Flutter — Dart; draws its own widgets; pixel-identical across platforms; excellent performance; new language and toolchain to learn.
- Ionic — web tech (HTML/CSS/JS); runs in a webview with native bridges; maximum reuse of existing web skills and code; closest to "just ship the web app as a mobile app," with a webview's performance ceiling.
The question that picks between them isn't "which is best" — it's "what am I already fluent in, and how native does the result have to feel." Coming from React and wanting authentic platform UI: React Native. Wanting one high-performance, perfectly consistent UI everywhere and willing to learn Dart: Flutter. Already sitting on a web app and needing it in an app store by next week: Ionic.
How I use this
The habit these notes left me with is refusing to compare the three on a single axis. The real decision is two questions asked together — what language do I want to write, and who draws the pixels. Answer those two and the framework usually picks itself: React skills plus native components points at React Native; a new language plus a self-rendering engine points at Flutter; existing web code plus a webview points at Ionic. There's no wrong answer, just three different bets on the same trade-off.
References
[1] roadmap.sh, "Mobile Apps," 2024. [Online]. Available: https://roadmap.sh/frontend/mobile-apps
[2] Meta Open Source, "React Native — Learn the Basics," React Native Docs, 2024. [Online]. Available: https://reactnative.dev/docs/getting-started
[3] Google, "Flutter — Build apps for any screen," Flutter Documentation, 2024. [Online]. Available: https://docs.flutter.dev
[4] Ionic, "Ionic Documentation — Introduction," 2024. [Online]. Available: https://ionicframework.com/docs/intro
Knowledge check · Question 1 of 5
What does React Native do with your JavaScript UI code?
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!