Choosing a cross-platform framework: the questions to settle first

Native, Flutter, React Native or Kotlin Multiplatform: six questions about team, interface, platform APIs, size and releases that settle the choice early.

Choosing a cross-platform framework: the questions to settle first

Every framework debate I have sat through, in offices, on video calls and once on a delayed train between Rotterdam and Utrecht, started with the wrong question. Someone asks which framework is best. That question has no answer, which is why the debate lasts until somebody orders pizza.

The useful question is narrower: which option fits this team, this app and the next three years of maintaining it. You can settle most of that on paper before anyone writes a line of Dart, TypeScript or Kotlin. This guide is the paper. It reflects the state of things at the end of May 2024, after a busy month of announcements, and it compares four options: fully native, Flutter, React Native and Kotlin Multiplatform.

What is on the table in May 2024

Native

Two apps. Swift for iOS (SwiftUI, with UIKit where SwiftUI runs out), Kotlin for Android (Jetpack Compose, or the older View system). You write most things twice, and nothing stands between your code and the platform. Every new OS feature is available on day one, because your code is already speaking the platform's language.

Flutter

Google's framework uses Dart and draws its own interface with its own rendering engine instead of using the platform's buttons and lists. Flutter 3.22 arrived in mid May with a complete Vulkan backend for the Impeller renderer on Android (Impeller is already the default on iOS) and WebAssembly support on the stable channel for web builds. One codebase, one widget tree, and pixels that look the same everywhere unless you make them differ.

React Native

Meta's framework uses JavaScript or TypeScript with React and renders real platform views: a React Native list on Android is an Android view underneath. Hermes is the default JavaScript engine. The New Architecture, which replaces the old asynchronous bridge between JavaScript and native code, moved to beta in May, announced at React Conf. Many teams now start projects with Expo, which handles builds, native modules and over-the-air updates.

Kotlin Multiplatform

JetBrains' approach shares Kotlin code across platforms and leaves the UI decision to you. It has been stable since Kotlin 1.9.20 in November 2023, Kotlin 2.0 with the new K2 compiler shipped on May 21, and at Google I/O Google announced official support for sharing business logic between Android and iOS, with DataStore already supporting it and Room following in alpha. If you want shared UI as well, JetBrains' Compose Multiplatform promoted its iOS target to beta in version 1.6.10.

Question 1: who is on the team now, and who will be in two years

Frameworks are chosen by the people already in the room and maintained by people who are not there yet. Both groups count.

If your team is three web developers who live in React, React Native lets them be productive in the first week. They will still meet Xcode signing, Gradle files and CocoaPods, usually on a Friday afternoon, so plan for at least one person who is not afraid of them. If your team is Android developers (hello), Kotlin Multiplatform lets them share the data layer without learning a new language, while iOS colleagues keep writing SwiftUI. If nobody has a strong attachment to anything, Flutter is the easiest to learn as a whole, because Dart and the widget system come as one package.

Then think about hiring. React and TypeScript experience is common, but developers who combine it with real iOS and Android knowledge are rarer, and you want at least one. Dart lives almost entirely inside Flutter, so you are hiring Flutter developers specifically. Swift and Kotlin developers are a known market, and a Kotlin Multiplatform codebase is readable to any Android developer, though your iOS hires need to be comfortable consuming a framework built from Kotlin. If you are still working out which roles the project needs at all, the guide to the specialists an app project needs is a sensible companion to this one.

Question 2: how native must the interface feel

Write down which parts of your interface must behave exactly like the platform: text selection, scrolling physics, keyboard handling, accessibility focus order, the share sheet, date pickers. Then write down which parts are your own brand anyway, like a custom onboarding or a playful home screen.

Native code and React Native use the platform's own components, so system behavior comes along, including changes Apple and Google make in new OS versions. Flutter reproduces platform behavior in its own Material and Cupertino widgets. The reproduction is very good, but it is a reproduction, and when the platform changes, Flutter has to catch up. That matters less if your design is heavily branded and meant to look identical on both platforms.

Kotlin Multiplatform with native UI gives you full platform fidelity, at the cost of building each screen twice. Compose Multiplatform on iOS trades some of that fidelity for shared UI code, on a target that has only just reached beta. I would not bet a banking app on that yet. A side project, gladly.

Question 3: which platform APIs you lean on

Make a list of every device or OS capability the app touches: Bluetooth, background location, HealthKit or Health Connect, home screen widgets, Live Activities, in-app purchases, a camera with manual controls, notification extensions. This list decides more than anything else in this article.

Each cross-platform option has a door to native code. Flutter has platform channels, plus Pigeon, which generates type-safe channel code for you. React Native has native modules, Turbo Modules under the New Architecture, and the Expo Modules API for writing them in Swift and Kotlin. Kotlin Multiplatform has expect and actual declarations and can call Apple's frameworks directly from Kotlin on the iOS side.

The question is how often you will walk through that door. Some features, such as iOS widgets and Live Activities, run as separate extensions and need Swift in any setup. If half your list needs the door, you are writing a native app with extra steps and an extra build system.

Question 4: does app size matter to your users

Flutter and React Native both ship a runtime inside your app: the engine in Flutter's case, a JavaScript engine plus framework code in React Native's. A small native app will start from a lower floor. Kotlin Multiplatform compiles shared code into a native framework for iOS, which includes the Kotlin/Native runtime, and on Android it is simply Kotlin.

For many apps this does not matter. It does matter if your users are on prepaid data, on older phones with little free storage, or in markets where a large download is reason enough to install a competitor. Do not argue about it in the abstract. Build a release version of a near-empty app with each option on your shortlist and measure it. Flutter's build tool has an analyze-size option and DevTools has an app size view, and on Android, whatever you choose, ship an Android App Bundle so Google Play delivers only what each device needs.

Question 5: how often you plan to ship

Release cadence is where React Native has a real structural difference. Because much of the app is JavaScript, teams can push updates to that code over the air, through Expo's update service or a self-hosted setup, without waiting for a store review. Both stores have rules about downloaded code, so read them before you build a release plan on this. Microsoft has also announced that App Center, the long-time home of CodePush, shuts down at the end of March 2025, so check what any tutorial you follow depends on.

Flutter, native and Kotlin Multiplatform apps go through the stores for every change to compiled code. That sounds slower than it is: with automated builds and phased rollouts, a weekly store release is routine for plenty of native teams. The other half of cadence is how fast you must adopt new OS features. Native teams get them when Apple and Google ship the SDKs. Cross-platform teams wait for the framework or a plugin, or write the native part themselves, which brings you back to question 3.

Question 6: who maintains it, and how upgrades feel

Flutter is Google's, React Native is Meta's, and Kotlin is developed by JetBrains, with Google as a co-founder of the Kotlin Foundation. All three are open source with large communities, so the risk of a framework disappearing is smaller than the risk of a painful upgrade.

Upgrades are where the time goes. React Native upgrades have historically needed care, which is why the community maintains an Upgrade Helper that shows the diff between versions, and the New Architecture migration is the big one ahead. Flutter upgrades are usually calmer, but plugins can lag behind. Kotlin upgrades touch the Gradle build, and anyone who has bumped the Android Gradle plugin knows that feeling. I keep a recurring calendar entry for it called "dependency day", and nobody has ever tried to take it from me.

The four options side by side

Question Native Flutter React Native Kotlin Multiplatform
Language Swift and Kotlin Dart JavaScript or TypeScript Kotlin, plus Swift for iOS UI
What is shared Nothing by default UI and logic UI and logic Logic; UI optional
How UI is drawn Platform components Own rendering engine Platform components Native UI or Compose Multiplatform
Platform fidelity Full Reproduced in widgets High, follows the platform Full with native UI
New OS features Day one Via framework or plugins Via framework or modules Day one in native UI
Hiring pool Swift and Kotlin developers Flutter developers React developers plus native know-how Android and iOS developers
Updates outside the store No No JavaScript, within store rules No
Existing native app Not applicable Add-to-app Can be embedded One module at a time
Typical fit Platform-heavy, long-lived apps Branded UI, one small team React teams, frequent changes Android teams sharing rules

A one-afternoon decision process

  1. Write the platform API list from question 3 and mark each item "plugin exists" or "native code needed". Check that plugins have had releases in the past few months, not just a lot of stars.
  2. Write down the team you have and the next two people you expect to hire.
  3. Pick the one screen that combines your hardest UI and your hardest platform API.
  4. Build that screen in your top two options, with the same number of days for each.
  5. Measure release build size and cold start on the oldest phone you intend to support.
  6. Upgrade each prototype to the newest framework version and a few dependencies, and note what breaks.
  7. Rough out one codebase against two with the app development cost calculator, decide, and write the reasons in the repository so the next person knows why.

If you only do one of these, do step 4. Two days spent on the screen that scares you most will tell you more than two weeks of comparison articles, this one included. Keep the losing prototype in a branch, too: the next time someone asks why you did not pick the other framework, you can hand them the code instead of starting the debate again. More of this kind of build-side thinking lives on the mobile development track.