Home  /  Mobile OS & Software  /  Cross-Platform Benchmark
Mobile OS & Software — Benchmark Report

Cross-Platform Framework Benchmark: React Native vs Flutter vs KMP

By David Okonkwo, Embedded Systems Engineer August 12, 2026 12 min read Updated for React Native 0.76, Flutter 3.24, KMP 2.1

For three years the question was philosophical — “which cross-platform framework feels better?” In 2026 it’s empirical. React Native’s New Architecture is production-default, Flutter’s Impeller is stable, and Kotlin Multiplatform (KMP) has moved beyond logic-sharing into Compose Multiplatform. We benchmarked the same production-grade app on all three to answer: where does the silicon actually spend its cycles?

We built a reference app — feed with images, paginated list (5,000 items), offline SQLite, WebSocket sync, camera capture, and background audio processing — implemented idiomatically in each stack. All builds were release-signed, R8/ProGuard enabled, Hermes AOT for React Native, AOT + Impeller for Flutter, and Kotlin/Native + Swift interop for KMP on iOS. Devices: Pixel 7a (Tensor G2, Android 14), Galaxy A54 (Exynos 1380), and iPhone 13 (A15, iOS 17.6). Profiling via Perfetto, Xcode Instruments, and Android Macrobenchmark 1.2.

Bottom line up front: Flutter wins raw startup and frame consistency; React Native New Architecture closes the bridge gap and wins on binary size and web reuse; KMP wins on native fidelity and long-term maintainability when you can afford two UI layers. The right choice is workload-dependent, not ideological.

1. Architecture Is The Benchmark

Performance differences don’t come from language speed. They come from how many threads, copies, and serialization steps sit between a user gesture and a pixel.

React Native 0.76: Fabric, TurboModules, JSI

The legacy bridge is gone. Fabric renders via C++ Shadow Tree and commits to the host platform synchronously. JavaScript (Hermes) talks to native via JSI — direct memory references, not JSON. TurboModules are lazy-loaded, typed, and synchronous when needed. The result: layout and animation work that previously bounced across three queues now stays on the JS thread or hits the UI thread without queuing. In our trace, time-to-first-commit dropped 32% vs. the old architecture. This matters most for gesture-driven UIs, as we explored in mobile DSP audio processing pipelines where low-latency callbacks are critical.

Flutter 3.24: Impeller + Dart AOT

Flutter owns everything. Dart AOT compiles to ARM64, Impeller precompiles shaders, and the engine composites directly via Vulkan/Metal without an OEM widget layer. There is no bridge — platform channels are message-based but rarely on the hot path. Skia/Impeller draws to a single surface, which eliminates the view inflation cost Android pays for Compose/View. The tradeoff: you carry the engine (~4.2 MB compressed) and duplicate platform accessibility and text logic. For custom graphics, this ownership pays off dramatically.

Kotlin Multiplatform 2.1: Shared Logic, Native UI

KMP is not a UI framework. You share domain, networking, and persistence (Ktor, SQLDelight, Korge) and build UI natively — SwiftUI on iOS, Compose on Android — or use Compose Multiplatform for a shared UI. We tested both. Native UI + shared ViewModel was the most predictable for memory and updatability; Compose Multiplatform matched Flutter’s consistency but added ~1.8 MB and a second composition model to debug. For hardware-adjacent work like smartphone PCB design principles where sensor timing is exposed via platform SDKs, KMP’s direct native API access without wrappers is a real advantage.

2. Methodology & Test Harness

We avoided “Todo app” benchmarks. They hide scheduler, GC, and IO costs. Our benchmark app includes:

Mobile Connectivity Standards Comparison
Feature 5G NR Wi-Fi 7 Bluetooth 5.4
Peak Throughput20 Gbps46 Gbps2 Mbps
Latency1–4 ms1–2 ms3–6 ms
RangeSeveral km30–50 m100–400 m
Power DrawHighMediumUltra-low
Primary UseCellular dataLocal networkingPeripherals/IoT

Each scenario ran 20 cold starts (process kill + cache drop) and 50 iterations for frame metrics. We report median and p95. Builds used identical assets, minified identically, and network mocked locally to isolate rendering. Memory was PSS after 5 min idle + after stress. Power was measured via Battery Historian and Instruments Energy Log during 10-min scroll.

Important nuance: KMP numbers are bimodal. KMP + Native UI and KMP + Compose Multiplatform behave differently. We report both where relevant, but table below uses KMP (Native UI) except where noted as “KMP-CMP”.

3. Results: Startup, Jank & Memory

Raw numbers first, then what they mean for the hardware.

Metric (Release) Flutter 3.24 (Impeller) React Native 0.76 (Hermes + Fabric) KMP + Native UI / CMP
Cold start Pixel 7a / iPhone 13 620 ms / 480 ms Fastest 740 ms / 560 ms 690 ms / 520 ms  |  810 ms / 610 ms (CMP)
Warm start (activity resume) 110 ms 135 ms 105 ms (Native best)
List scroll 60fps, % frames >16.6ms 1.2% jank 2.8% jank 1.9% / 3.1% (CMP)
Nav transition p95 frame time 14.1 ms 15.8 ms 13.4 ms (Native)
PSS Memory idle / stress 210 MB / 340 MB 185 MB / 310 MB Leanest 168 MB / 285 MB (Native) Leanest
APK size (arm64, split) / IPA 19.2 MB / 28.5 MB 12.8 MB / 19.1 MB 13.5 MB / 18.4 MB
JS/Dart GC pauses p99 4.2 ms (Dart) 6.8 ms (Hermes) 3.1 ms (Kotlin/Native)
Power 10-min scroll (mAh) 48 mAh 44 mAh 41 mAh (Native)

What to read from this: Flutter’s cold start lead comes from AOT + no view inflation — the engine is already hot. React Native lags 100–120 ms due to Hermes bytecode load + Fabric mount, but its gap shrank from ~400 ms in 2023. KMP with native UI is warm-start king because there’s no runtime to hydrate; it’s just an Activity/ViewController.

For sustained rendering, Flutter’s Impeller eliminated shader compilation jank that plagued Skia — our p95 improved 38% over Flutter 3.10. React Native’s worst frames were still layout thrash on rapid state updates; using useLayoutEffect and FlashList mitigated it. KMP native had the smoothest navigation because it uses the platform’s native animator and doesn’t contend with a cross-thread reconciler.

Memory tells a silicon story: Flutter holds two trees (Widget + RenderObject) plus engine; React Native holds JS heap + Shadow Tree but benefits from Hermes’ compact bytecode; KMP native holds only the platform view tree plus a modest Kotlin runtime. After 10k image items, KMP peaked lowest. If your app handles privacy-sensitive image caching, review mobile privacy tracking prevention — our cache encryption layer added 7 MB PSS uniformly across frameworks.

⚡ Startup Winner: Flutter

Best when first impression matters — retail, onboarding, low-end Android. Impeller’s precompiled shaders remove the first-scroll stutter.

🧠 Memory Winner: KMP Native

Best for long-lived sessions and background work — messaging, health, audio. Native views GC with the OS.

4. Interop & Developer Ergonomics

Benchmarks ignore the cost of shipping. As an embedded engineer, I care about how fast I can drop to C/C++ or platform APIs without rewriting.

Calling Native Is Now The Differentiator

React Native’s Codegen + TurboModules makes typed, synchronous native calls trivial. You define a spec and get TypeScript + C++ bindings. Flutter uses MethodChannel (async) or FFI for sync; it’s fast but requires manual codec work. KMP uses expect/actual and direct Swift/ObjC interop — the most natural when wrapping an existing SDK.

React Native — TurboModule spec (TypeScript)Codegen → C++ + Java
// NativeAudioModule.ts — typed, lazy-loaded
import type { TurboModule } from 'react-native';
export interface Spec extends TurboModule {
  setEqBand: (band: number, gainDb: number) => void;
  getLatencyMs: () => number;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('AudioEQ'));
KMP — expect/actual (Kotlin)Shared logic • Native impl
// commonMain
expect class SecureStore {
  fun read(key: String): String?
}
// androidMain — backed by Keystore, iOSMain — Keychain
actual class SecureStore actual constructor() {
  actual fun read(key: String) = keystoreGet(key)
}
Flutter — FFI + Platform ChannelDart AOT → C
final dylib = DynamicLibrary.open('libdsp.so');
final process = dylib.lookupFunction<Int32 Function(Float), int Function(double)>('dsp_process');
// For async platform APIs: MethodChannel('audio_eq').invokeMethod('setBand', {...})

In practice: React Native has the largest npm surface and best web reuse via React Native Web. Flutter has the most consistent design system out of the box, but platform integration (background modes, widgets, intents) needs plugin maintenance. KMP has the smallest shared-library risk — if Compose Multiplatform stalls, your shared domain still ships — but it demands native UI talent.

Build times reflect this: Flutter incremental ~12s, React Native (with Hermes bytecode cache) ~18s, KMP dual-target ~24s on M2 MacBook Pro. CI pipeline complexity is inverse: KMP needs Xcode + Gradle truth; Flutter needs one toolchain; React Native needs Node + Ruby + Gradle + Xcode.

5. Verdict: Which To Ship?

There is no universal winner. Choose by constraint:

Pick Flutter if you need pixel-perfect brand UI, 120 Hz animations, and fastest cold start on mid-tier Android, and can tolerate larger binaries. It’s the safest “one UI codebase” bet in 2026, especially with Impeller stable.

Pick React Native (New Architecture) if you have React/web assets, need smallest downloads, or rely on large native module ecosystem. Fabric+JSI finally solves the bridge tax; Hermes p99 GC is still slightly worse than Dart but not human-perceptible. It pairs well when you already instrument TLS pinning and certificate rotation in JS.

Pick KMP if you need native platform fidelity, OS-level integrations, or you’re incrementally sharing code from existing native apps. For regulated or hardware-adjacent apps where PCB-sensor behavior matters (see PCB design integration), KMP’s zero-wrapper platform access and leanest memory footprint are decisive. Use Compose Multiplatform only if you commit to a design system that justifies sharing UI.

From a silicon perspective, framework choice is less about “which is faster” and more about “where your jank budget lives.” Flutter pays upfront in binary size; React Native pays in GC and mount; KMP pays in duplicated UI effort. Profile on your lowest-supported device, not your flagship — the Pixel 7a delta was 1.7× the iPhone 13 delta.

Recommendation for 2026 greenfield: If you must ship one UI with smallest team — Flutter. If you have web + native bridge needs — React Native New Architecture. If you’re modernizing large native apps — KMP. All three clear 60fps today; the difference is operational, not just milliseconds.

Frequently Asked Questions

Which framework has the fastest cold start in 2026?

In our benchmark on mid-range Android 14 and iPhone 13, Flutter had the fastest cold start (620 ms Android / 480 ms iOS) due to AOT compilation and no JS bridge. React Native with Hermes was 740 ms / 560 ms, KMP Native 690 ms / 520 ms.

Does React Native's New Architecture close the gap with Flutter for 60/120fps?

Yes. With Fabric, TurboModules, and JSI, the async bridge serialization is removed. React Native hit 59–60 fps in list and nav tests vs 60 fps for Flutter. Flutter still leads for complex custom animations that benefit from owning the full Impeller pipeline.

Is Kotlin Multiplatform a replacement for Flutter or React Native?

No. KMP shares business logic, data and networking; UI stays native or uses Compose Multiplatform. Use it when you need native UX fidelity and shared logic without platform compromises, not as a drop-in single-UI solution.

Which framework produces the smallest binary size?

React Native with Hermes produced the smallest release builds (~12.8 MB APK, ~19.1 MB IPA), followed by KMP (~13.5 MB). Flutter was largest (~19.2 MB APK) due to bundling the engine, though its size grows more slowly as features are added.

References & Standards: GSMA Intelligence · Bluetooth SIG