A Few Good CodersStart a project
White paper

Native, Flutter, React Native, KMP or Web: How to Choose

Choose the stack your product needs, then check your team can own it.

For founders, product leaders and engineering leaders deciding how to build an app across iOS, Android and the web.

Updated , by A Few Good Coders

Published 13 min read11 sectionsWhite paper

Key takeaways

  • No option wins everywhere: native leads on platform depth, Flutter and React Native on shared UI, Kotlin Multiplatform on shared logic under native UI, and the web on reach and discoverability.
  • Hard constraints such as widgets, watch apps, background Bluetooth or search indexing should rule options out before any weighted scoring begins.
  • Team skills and long-term ownership often move the result more than raw performance, so weight them honestly.
  • The hidden costs of cross-platform sit at the edges: plugins, platform channels, app size, accessibility of custom UI and web limitations; native's hidden cost is drift between two apps.
  • Scores within about 10% of each other are a tie, best settled by a two-week vertical slice on real devices rather than a debate.

The short answer

Pick the option whose weaknesses your product can live with. Native (Swift and Kotlin) is the default when platform features lead the product. Flutter and React Native fit when most behaviour and UI are shared across iOS and Android. Kotlin Multiplatform fits when you want native UI over shared business logic. The web, including a progressive web app (PWA), fits when reach, links and search matter more than deep device access.

We build with Flutter often, and it is one of our services, but a stack chosen for the vendor's convenience costs the client for years. So this guide treats every option on its merits, names the costs each one hides, and ends with a worksheet you can fill in with your own team. Three rules run through it:

  • Constraints before preferences. An Apple Watch app or background Bluetooth decides more than any score.
  • The team is part of the stack. A framework your team cannot hire for or maintain is the wrong one.
  • Decide with evidence. When the options are close, build a thin slice and measure it.

The five options, defined

Each option trades how much code is shared against how close it sits to the platform. The table reflects October 2026; check current release notes before you commit.

OptionLanguageHow the UI is drawnWhat is sharedWhere it is strongest
NativeSwift (iOS), Kotlin (Android)Each platform's toolkit: SwiftUI or UIKit, Jetpack Compose or ViewsLittle by default; backend, API contracts and designPlatform-led features, new OS APIs from day one, the most platform-faithful UX
FlutterDartIts own rendering engine draws every pixelMost UI and logic across iOS, Android, web and desktopOne consistent brand UI and shared journeys across many screens
React NativeJavaScript or TypeScript, with ReactComponents map to native platform controlsMost UI and logic across iOS and Android; web through React Native for WebTeams with deep React experience who want native controls
Kotlin Multiplatform (KMP)KotlinNative UI per platform, or shared UI with Compose MultiplatformBusiness logic, networking and data; optionally UINative UX over one implementation of the rules, especially for Kotlin-strong teams
Web / PWATypeScript, HTML, CSSThe browserEverything, on any device with a browserReach, links, search visibility, updates without store review

Two terms matter for the rest of this guide. A platform channel is the bridge a cross-platform framework uses to call native code it does not wrap itself; Flutter calls these method channels and React Native calls them native modules. A PWA is a website that can be installed to the home screen and work offline through a service worker. What a PWA can do depends on the browser, and iOS is the most restrictive: Safari supports web push only for web apps the user has added to the home screen.

All five are mainstream production choices. Kotlin Multiplatform has been stable since late 2023, and JetBrains declared Compose Multiplatform stable on iOS in 2025. React Native's New Architecture is now the default for new projects. The decision is about fit, not maturity.

The decision criteria

Seven criteria decide most stack choices. Score your product against each before you look at frameworks, because the product's shape is what makes an option cheap or expensive.

CriterionQuestion to askTends to favour
Shared behaviour across screensHow much of each journey is identical on iOS, Android and other screens?Flutter, React Native, KMP or web when high; native when low
Platform-specific UXMust the app feel exactly like each platform, or carry one brand UI everywhere?Native, React Native or KMP with native UI for platform fidelity; Flutter for one consistent UI
Team skillsWhat does the team ship confidently today?Whatever matches: Swift and Kotlin, Dart, React and TypeScript, or Kotlin
Performance profileIs the hard part rendering, computation, startup time or the network?Native or KMP for tight platform-level work; any option for typical lists, forms and media
Access to device APIsDoes the product need widgets, a watch app, CarPlay, background location, Bluetooth or health data?Native first; cross-platform with native modules second; web last
Long-term ownershipWho maintains this in three years, and how many codebases can they carry?Shared-code options for small teams; native for organisations with platform teams
HiringCan you hire and replace people for this stack in your market?Check your own market; JavaScript and native talent pools are usually larger than Dart or KMP

Three notes on reading the table honestly.

Performance is rarely the deciding criterion. Most business apps are lists, forms, media and network calls, and every option here handles those well when engineered carefully. Performance decides the stack when the product does sustained custom rendering, heavy on-device computation or real-time audio and video. Then, prototype the hard screen on each candidate rather than trusting anyone's benchmark, including ours.

Platform-specific UX is a product decision. A financial app with a strong brand may want the same UI everywhere; a utility that sits next to system settings may want to look exactly like the OS. Decide which you are before a framework decides for you.

Hiring cuts both ways. A larger pool helps with replacement; a smaller one can be easier to screen. What really hurts is a stack only one person on the team understands.

When Flutter is the right call

Flutter is the right call when most of the product is shared journeys, one brand UI matters, and one team needs to ship to several platforms at the same rhythm. It is weaker when the product's value lives in platform features.

Signals that point to Flutter:

  • The journeys are the product. Onboarding, accounts, catalogues, workflows, payments and dashboards: behaviour that should be identical on iOS and Android.
  • One consistent UI is a feature. Flutter draws its own widgets, so a custom design system looks and behaves the same on every device, including older OS versions.
  • More than two screens are on the roadmap. Official web, Windows, macOS and Linux targets let a tablet or desktop companion reuse the domain logic and design system.
  • A small team owns everything. One codebase, one test suite and one release train are easier for four engineers to carry than two native apps.

Two products we have worked on show the shape. For BankSathi, we built the Flutter application architecture and the advisor journeys for product discovery, lead management, conversion tracking and payouts, shipped as production Android and iOS apps. For ScoreUp, we delivered the Flutter product across credit report visibility, guided explanations and next-action journeys, maintained on a shared Flutter foundation with a live Android release. Both are workflow products whose value lives in journeys that should behave identically for every user, not in hardware features. That is where a shared codebase earns its keep.

Flutter does not make a product cheaper by default. It removes duplicated work when the work is genuinely shared; platform-specific work is paid for in native code either way, plus the bridge.

When Flutter is not the right call

Flutter is the wrong default when platform features lead the product, when it must be found through search, or when the team's skills and hiring plan point elsewhere. In each case another option is simply a better fit.

If your product…Consider insteadWhy
Lives in home-screen widgets, watch apps, Live Activities, CarPlay or Android AutoNative, or native UI over shared logic with KMPThese surfaces need platform UI code in every cross-platform stack
Must follow each platform's conventions exactlyNative, React Native or KMP with native UIPlatform controls come built in; Flutter has to match or adapt them
Is content people find through search or open from shared linksServer-rendered webFlutter web draws into a canvas, which suits app-like screens behind a login more than indexable pages
Is built by a team of strong React and TypeScript engineersReact Native, or the webExisting skills, libraries and hiring pipeline transfer directly
Is Android-first with a strong Kotlin team, adding iOS laterKMPShare logic in the language the team already uses and keep native UI
Runs heavy on-device work through existing native or C++ librariesNative, or a thin UI over native modulesFewer layers between the product and the library

None of these is a rule against Flutter. A product can be mostly shared journeys with one platform-led feature, and the answer is then often Flutter plus a native module at a narrow boundary. The skill is noticing when the exception has become the product.

The hidden costs nobody prices in

Every option has costs that do not show up in the first demo. For cross-platform frameworks they sit at the edges, where shared code meets the platform. For native they sit in duplication. Price both.

Plugins and dependencies

Cross-platform apps reach device features through community or vendor plugins, and each one is a dependency that must keep up with every OS release, framework release and store policy change. Before choosing, list every device capability on the roadmap and check its plugin: who maintains it, when it was last updated, and what is open on your platforms. A capability with no healthy plugin is native code you will write.

Platform channels

When no plugin exists, your team writes the bridge in Swift and Kotlin as well as Dart or JavaScript. Pigeon generates type-safe Flutter bridges and dart:ffi calls C libraries directly. The real cost is needing someone who can debug a native build failure on release day, so budget for that skill even in a "pure" Flutter or React Native team.

App size

Flutter ships its rendering engine and React Native a JavaScript engine, so the smallest possible app is larger than the smallest native one. That rarely matters, except in markets with low-storage phones and expensive data. Measure your real build early with flutter build apk --analyze-size or your stack's equivalent.

Accessibility of custom UI

Native controls arrive with screen-reader behaviour built in. Flutter builds a semantics tree that maps to VoiceOver and TalkBack, and its standard widgets fill it in, but every custom-painted widget needs explicit semantics. React Native inherits more from native controls, yet custom components still need labels and roles. Whatever the stack, test with a screen reader and at 200% text size on real devices before launch.

Web caveats

Flutter web suits signed-in, app-like tools, not marketing or content pages that need fast first paint and search indexing. React Native for Web shares components with mobile but usually needs web-specific work for routing, layout and SEO. A PWA reaches everyone but depends on browser support for push, background work and hardware access, which is narrowest on iOS.

The hidden cost of native

Two native apps mean two implementations of every rule, two test suites and two release trains. The usual failure is drift: a fix lands on Android in March and on iOS in May, and support learns to ask which phone the customer has. If you choose native, budget for parity explicitly with shared specifications, shared API contracts and, where it fits, shared logic through KMP.

A scoring worksheet you can use

Use the worksheet in two passes: rule options out with hard constraints, then score what is left. It takes an afternoon with the product lead and two engineers.

Pass 1: vetoes. Answer yes or no. Any yes can remove an option outright.

  1. Does the product need a home-screen widget, watch app, CarPlay or Android Auto in its first year? (Plan native UI for that surface in any stack.)
  2. Does it need background Bluetooth, background location or continuous sensor access on iOS? (Rules out a PWA.)
  3. Must its pages be indexed by search engines or open instantly from shared links? (Rules out Flutter web and app-only options for those pages.)
  4. Must the new work live alongside an existing app that already has users? (Favours incremental routes: Flutter add-to-app, React Native in an existing app, or a KMP module inside it.)
  5. Is there a regulatory or contractual accessibility requirement? (Make it a prototype test for every option.)

Pass 2: weighted scores. Give each criterion a weight from 1 (barely matters) to 5 (decides the product). Then score each surviving option from 1 to 5: 5 means a strong fit with no workaround, 3 means workable at a known cost, 1 means you would be fighting the option. Multiply weight by score and add up each column.

Weighted score = Σ (criterion weight × option score)

Here is an illustrative example: a hypothetical booking product with a consumer app on iOS and Android and a staff dashboard on the web, built by three engineers with React and TypeScript experience and one with Kotlin. The numbers are invented to show the method, not a verdict on any framework.

CriterionWeightNativeFlutterReact NativeKMPWeb / PWA
Shared behaviour across screens525544
Platform-specific UX253442
Team skills423525
Performance profile254453
Access to device APIs254452
Long-term ownership434434
Hiring333535
Total (out of 110)69841007785

Three things to notice:

  • Team skills moved the result. Give the same product a team with Flutter experience and Flutter's team-skills score rises from 3 to 5, adding 8 points and taking it to 92. The product did not change; the people did.
  • Close scores are a tie. Flutter at 84 and the web at 85 are indistinguishable. Treat anything within about 10% of the leader as a candidate worth testing.
  • Vetoes still apply. If the booking product depends on reliable reminders on iOS, the PWA's reliance on users installing it to the home screen for web push may rule it out whatever it scores.

Keep the filled-in worksheet with the project's decision records, so the answer to "why is it built this way?" is a document, not a memory.

Hybrid and migration paths

The choice is rarely all or nothing. Most mature products end up hybrid, and every mainstream option supports incremental adoption. Choose the path that keeps the decision reversible.

  • Flutter add-to-app. Embed Flutter screens in an existing native iOS or Android app, one journey at a time. It is a low-risk way to test Flutter on a real feature before committing the product.
  • React Native in an existing app. The same idea: a native shell, with React Native screens where they help.
  • KMP inside native apps. Move networking, validation and business rules into a shared Kotlin module while both native UIs stay as they are. Often the cheapest way to stop drift between two native apps.
  • Native modules at a narrow boundary. In a Flutter or React Native app, keep platform-led features such as a widget, a camera pipeline or a payments SDK in native code behind one well-defined interface.
  • Split by surface. Server-rendered web for marketing and content, a cross-platform or native app for the signed-in product, and one API behind both.

Avoid the big-bang rewrite if you can: it pauses the roadmap while users keep filing bugs against the old app, whereas incremental paths keep shipping while you learn whether the new stack holds up. Our guide to taking over a codebase covers how to assess an existing app before you decide.

Run the decision in two weeks

When the worksheet produces a close call, build a thin vertical slice in the top two candidates and measure it. Two weeks of evidence costs less than two years of regret.

A useful slice is one real journey, not a demo:

  1. Pick the riskiest journey. Usually sign-in plus the screen with the hardest device integration or the heaviest rendering.
  2. Use real infrastructure. Real authentication, the real API, real analytics, and a build pipeline to TestFlight and the Play internal testing track.
  3. Test on the devices your users own. Include an older, lower-end Android phone, not only the team's flagships.
  4. Measure what the worksheet guessed. Cold start, smoothness on the heaviest list, build size, screen-reader behaviour, and how long the device integration took to get working.
  5. Ask the team. Which codebase would they rather be maintaining in a year, and why?

Update the worksheet with what you measured, make the call, and note what would make you revisit it.

If you want a second opinion, tell us about the product. We start with a call on fit and risk. If Flutter is the right tool, our Flutter app development work begins with one complete journey released in a bounded first milestone; if it is not, we will say so. Once you have chosen, our guide to architecture that survives year two covers what to build next.

Questions

Is Flutter cheaper than building two native apps?

It can be, but only for the work that is genuinely shared. Shared journeys, UI and logic are built and tested once, while platform-specific features still need native code and plugins add maintenance. Estimate how much of your roadmap is identical across platforms before assuming any saving.

Should we choose Flutter or React Native?

Both are mature and share most code across iOS and Android. Flutter draws its own UI, which suits one consistent brand experience and extends to web and desktop targets. React Native uses native platform controls and fits teams with strong React and TypeScript skills. Team skills and UI goals usually decide it more than performance does.

Is Kotlin Multiplatform ready for production?

Kotlin Multiplatform has been stable since late 2023 and is used to share business logic between Android and iOS while keeping native UI. Compose Multiplatform adds optional shared UI and was declared stable on iOS in 2025. It fits best when the team already knows Kotlin; check library support for your specific needs, as of October 2026.

Can a progressive web app replace a native app?

For many content, booking and internal tools, yes: a PWA can be installed, work offline and update without store review. It falls short when the product needs deep device access, background work or dependable push on iOS, where web push requires the user to add the app to the home screen first. Run the veto questions before choosing it.

Is Flutter good for building websites?

Flutter web is a good fit for signed-in, app-like tools such as dashboards and internal workflows that share code with a mobile app. It is a poor fit for marketing or content sites that need fast first paint and search indexing, because it renders into a canvas rather than ordinary HTML pages. Many products pair a Flutter app with a separate web stack for public pages.

Should we rewrite our native app in Flutter?

Only if the evidence supports it. Measure how much of the roadmap is shared, then try Flutter's add-to-app on one real journey inside the existing app. If drift between two native apps is the real problem, sharing logic with Kotlin Multiplatform may fix it with less disruption.