Cross-Platform Frameworks Compared
Flutter vs React Native vs Kotlin Multiplatform: three different bets about what to share.
1. The problem, and the one question that organises everything
Building the same app twice (Kotlin for Android, Swift for iOS) roughly doubles the work, and the two versions drift out of sync unless someone actively polices them. Every cross-platform approach is an answer to one question:
Which layer do we share, and which do we keep platform-specific? Flutter usually shares the UI and logic, then renders the UI itself. React Native shares React components and logic, then renders through native host components. Kotlin Multiplatform lets you share selected logic and, optionally, the UI too. Every trade-off flows from where you draw that boundary.
2. The three contenders
Flutter (Google)
Share the UI and logic; render it yourselfWritten in Dart. For its normal UI, Flutter bypasses the operating system's widget libraries and uses its own widget set and rendering engine. It can still embed native controls through platform views when a feature requires one.
- Upside: highly consistent rendering and excellent control over custom interfaces. Hot reload, strong typing, broad platform targets, and Google backing make the tooling compelling.
- Downside: Flutter must reproduce new platform conventions in its own widgets. Dart has a much smaller ecosystem outside Flutter, and platform APIs such as Bluetooth, Keychain, or health data require plugins, platform channels, or native code.
- Used by: Google Pay, BMW, Alibaba, eBay Motors.
- 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 React components and logic; use native hostsWritten in JavaScript/TypeScript with React. React Native turns components into native host components rather than drawing an entire Flutter-style canvas. Its New Architecture replaces the old serialized bridge with direct JSI-based communication and the concurrent Fabric renderer, supporting synchronous or asynchronous native access as appropriate.
- Upside: ecosystem gravity. React knowledge transfers, some non-UI code can be shared with a website, and the npm ecosystem is enormous. Expo provides a widely used production toolchain for builds, native modules, and over-the-air updates to compatible JS and assets, within store policies.
- 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)
Choose exactly how much Kotlin to shareKMP lets a team share networking, storage, sync, domain logic, or other isolated modules in Kotlin. Shared code uses Kotlin/JVM on Android and Kotlin/Native on iOS. The UI can remain SwiftUI/UIKit on iOS and Compose/Views on Android, be shared with Compose Multiplatform, or mix both approaches.
- Upside: unusually fine-grained adoption: a team can start with one shared data or sync module and expand later, without replacing either UI. Google officially supports KMP for sharing business logic between Android and iOS.
- Downside: retaining two native UIs means less code reuse; sharing the UI changes that trade-off. Kotlin/Swift interoperability and Kotlin-built frameworks add friction for iOS developers. Compose Multiplatform is stable on iOS, but its ecosystem is younger than the native UI ecosystems.
- 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 | Flutter engine and widget set; native views can be embedded | React components rendered through native host components | Native UI, shared Compose UI, or a mixture |
| Typically shared | UI declarations and application logic | React UI declarations and application logic | Whatever the team chooses: isolated modules through to UI |
| Platform feel | Deliberately reproduced or custom-designed | Native hosts, with behaviour styled by the app | Native with platform UIs; consistent with shared Compose UI |
| Performance | Often excellent, especially for custom rendering | Strong for typical product UIs; architecture still matters | Shared logic can approach native performance; UI choice matters |
| Beyond mobile | Web + Windows/macOS/Linux from same code | Web via react-native-web; desktop niche | Server, desktop, web (logic); UI via Compose MP |
| Skill transfer | General mobile skills transfer; Dart is usually new | React/TypeScript transfer; native mobile knowledge is still needed | Strong for Android/Kotlin teams; iOS interop must be learned |
| Incremental adoption | Add-to-app modules | Individual views, screens, or flows | Especially flexible for isolated logic modules |
| Backed by | Meta (+ Microsoft, Expo) | JetBrains (+ Google endorsement) | |
| Main risk | Platform conventions can lag; smaller Dart ecosystem | Dependency churn and upgrade work | Kotlin/Swift interop and a younger shared-UI ecosystem |
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 knowledge shared with the website beat the alternatives' technical niceties |
| Existing native apps and native team | Kotlin Multiplatform | Adoptable module by module: stops the duplicate-logic bleeding without a rewrite or touching either UI |
| 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 Mobile 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 can use it the day the SDK lands; cross-platform teams wait for the framework to catch up or write the native 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. UI frameworks evolve, but a well-designed API can serve every one of them.
Check your understanding
- Which layer does each of the three frameworks share, and which does it keep native?
- How can each option be introduced into an existing native app, and why is KMP especially flexible for sharing isolated logic?
- Name two parts of the app stack that stay identical no matter which framework you pick.
Sources and further reading
Reviewed 6 August 2026.