Cross-Platform Frameworks Compared
Flutter vs React Native vs Kotlin Multiplatform: three different bets about what to share.
After this lesson you can explain
- Which layer each framework shares (pixels, widgets, or logic only), and why every strength and weakness flows from that one choice.
- Why the team you already have, not a benchmark, usually decides between Flutter, React Native, and Kotlin Multiplatform.
- What stays identical regardless of framework: the API client, token auth, push, and the release pipeline.
1. The problem, and the one question that organises everything
Building the same app twice (Kotlin for Android, Swift for iOS) doubles cost and guarantees the two versions drift out of sync. Every cross-platform framework is an answer to one question:
Which layer do we share, and which do we keep native? Flutter shares everything down to the pixels. React Native shares the logic and borrows the platform's widgets. Kotlin Multiplatform shares the business logic and keeps the UI fully native. Every strength and weakness of each framework flows from its answer.
UI pixels UI widgets Business logic Language Fully native x2 native native written twice Kotlin + Swift Kotlin Multiplatform native native SHARED Kotlin (+ Swift UI code) React Native native SHARED* SHARED JS/TS (*real native widgets, Flutter SHARED SHARED SHARED Dart driven from shared JS) Web wrapper SHARED SHARED (web) SHARED JS/TS + HTML/CSS
2. The three contenders
Flutter (Google)
Share everything, including the pixelsWritten in Dart. Flutter does not use the platform's UI widgets at all: it ships its own rendering engine and draws every pixel itself, the way a game engine would. A Flutter button is a Flutter-drawn picture of a button.
- Upside: total consistency. The app looks identical on Android, iOS, web, and desktop, and never waits for a platform widget to catch up. Excellent tooling (hot reload, strong typing) and first-class Google backing.
- Downside: the same coin's flip side. The app is only ever as native-feeling as Flutter's imitation; when Apple ships a new design language, Flutter must re-implement it. Dart is a you-only-use-it-here language. Platform APIs (Bluetooth, Keychain, health data) go through platform channels or community plugins.
- Used by: Google Pay, BMW, Alibaba, eBay Motors; the most popular choice for greenfield startup apps.
- Sweet spot: fresh starts with no web or native legacy, and brand-heavy custom UIs that would have been hand-drawn anyway.
React Native (Meta)
Share the logic, borrow the platform's widgetsWritten in JavaScript/TypeScript with React. Components render as real native widgets: an RN button on iOS is an actual UIKit button, driven from JS. The old asynchronous "bridge" that earned RN its slow reputation is gone, replaced by JSI and the Fabric renderer (JS calling native code synchronously), so the classic performance complaints are largely dated.
- Upside: ecosystem gravity. A React web team is productive in week one, knowledge (and some code) is shared with the website, and npm is the largest package ecosystem in existence. Expo, the de facto standard toolchain, packages builds, native modules, and over-the-air JS updates (ship fixes without a store review, within store policy limits).
- Downside: the JS world's churn comes along: dependency upgrades and breaking changes are a recurring tax. Truly custom rendering (canvas-heavy UIs) fights the model.
- Used by: large parts of Meta's own apps, Shopify (all-in), Microsoft (Office and Outlook surfaces), Discord.
- Sweet spot: web-first teams, and products where the website and app should feel like siblings.
Kotlin Multiplatform / KMP (JetBrains)
Share the business logic, keep the UI fully nativeInverts the bet. Networking, storage, sync, and domain logic are written once in Kotlin and compiled natively for each platform; the UI stays SwiftUI on iOS and Jetpack Compose on Android. No imitation layer anywhere.
- Upside: genuinely native apps on both platforms, with the duplicated-logic problem solved. Adoptable module by module: you can migrate one sync engine into shared Kotlin inside an existing native app, which neither Flutter nor RN can do gracefully. Officially supported by Google for Android teams.
- Downside: you still write two UIs, so it saves less than the other two. iOS developers must consume Kotlin-compiled frameworks, which adds friction. The optional Compose Multiplatform extension shares the UI too (Flutter-style); its iOS support is now stable but the ecosystem is still young.
- Used by: Netflix, McDonald's, Forbes, Cash App.
- Sweet spot: teams that already have two native apps and want to converge them gradually without giving up native UI.
3. Side by side
| Flutter | React Native | Kotlin Multiplatform | |
|---|---|---|---|
| Language | Dart | JavaScript / TypeScript | Kotlin (+ Swift for iOS UI) |
| UI rendering | Own engine draws every pixel | Real native widgets, driven from JS | Fully native UI per platform |
| Code shared | ~95–100% | ~85–95% | ~40–70% (logic only; more with Compose MP) |
| Native look and feel | Imitated (very good, not perfect) | Genuine widgets, JS-driven behaviour | Genuine, indistinguishable from native |
| Performance | Excellent; compiled, own renderer | Good; JSI closed the old gap | Native, by definition |
| Beyond mobile | Web + Windows/macOS/Linux from same code | Web via react-native-web; desktop niche | Server, desktop, web (logic); UI via Compose MP |
| Hiring pool | Growing; Dart learned on the job | Enormous (every React developer) | Android/Kotlin developers |
| Incremental adoption in an existing native app | Poor | Possible but awkward | Designed for it |
| Backed by | Meta (+ Microsoft, Expo) | JetBrains (+ Google endorsement) | |
| Main risk | Non-native feel where it matters; Dart lock-in | JS dependency churn; upgrade tax | Least code shared; iOS-side friction |
4. The decision logic
| Your situation | Pick | Because |
|---|---|---|
| Team already knows React / you have a React website | React Native (with Expo) | Week-one productivity and shared knowledge beat any technical nicety of the alternatives |
| Existing native apps and native team | Kotlin Multiplatform | Only option adoptable module by module; stops the duplicate-logic bleeding without a rewrite |
| Fresh start, no web legacy, design-led UI | Flutter | Fastest single-codebase route to a polished custom UI on both platforms |
| Product is really a website (content, forms, dashboard) | None: Capacitor or a TWA | Wrap the web app; a full framework rewrite buys little (see Web Apps vs Native Apps) |
| Value lives in cutting-edge platform features (ARKit, widgets, watch, health) | Fully native, twice | Cross-platform teams wait for wrappers; native gets platform features on day one |
The meta-point: all three are platform followers. When Apple or Google ships a new OS capability, native developers use it that day; cross-platform teams wait for the framework or write the glue themselves. The more of your app's value that lives in platform-specific features, the weaker the case for any cross-platform framework. The more your app is "UI over an API" (most apps), the stronger it gets.
5. What stays the same regardless of choice
Worth noticing across the whole course: the framework choice changes the UI layer and the language, and almost nothing else.
- The app is still an API client: same HTTPS, JSON, status codes, pagination.
- Auth is still token-based: secure storage, bearer headers, refresh rotation (the login-flow lesson applies verbatim in all three).
- Push still runs through FCM/APNs with the same registration and token-hygiene loop.
- Shipping is still the signed-bundle → review → staged rollout pipeline, with the same backwards-compatibility pressure on your API.
Which is the real conclusion: the backend and the API contract are the durable investment. Frameworks come and go on roughly five-to-ten-year cycles; a well-designed API outlives all of them.
Check your understanding
- Which layer does each of the three frameworks share, and which does it keep native?
- Why is Kotlin Multiplatform the only one of the three that can be adopted module by module inside an existing native app?
- Name two parts of the app stack that stay identical no matter which framework you pick.
Sources and further reading
Reviewed 4 August 2026.