How do native, React Native, and Flutter actually compare on real-world performance?
Synthetic benchmarks measure the wrong thing — production performance comes down to startup time, scroll jank, and memory pressure under real workloads.
LEARN BY DOINGPut your intuition to work3 steps
How do native, React Native, and Flutter actually compare on real-world performance?
Use the lesson’s examples and explanation to check your idea.
Personal reflection. Nothing is sent to an AI service.#The question behind the question
When an interviewer asks this, they are rarely fishing for "native is fastest, obviously." They want to know whether you understand why each approach performs the way it does — rendering model, threading model, and where the bottlenecks actually live — because that understanding is what lets you make a defensible framework choice for a specific product, and diagnose a slow screen once you've shipped it.
#Three different rendering philosophies
The performance ceiling of each approach is set by how it turns your code into pixels:
- Native (Swift/UIKit or SwiftUI on iOS, Kotlin/Jetpack Compose on Android) compiles directly to the platform's own widget toolkit. There is no intermediate layer between your layout code and the OS's rendering engine. Every animation, every scroll event, every gesture is handled by code the OS vendor tuned specifically for that hardware.
- React Native runs your application logic in a JavaScript engine (Hermes, by default, since RN 0.70+) and renders using the actual native widgets — a
<View>becomes a realUIVieworandroid.view.View. Historically, JS and native communicated over an asynchronous "bridge" that serialized every call to JSON and batched it across a message queue. The New Architecture (Fabric renderer + JSI + TurboModules) replaced that with direct, mostly-synchronous C++ bindings, removing the serialization tax for the vast majority of calls. - Flutter compiles Dart to native ARM/x64 machine code (AOT in release builds, JIT in development for hot reload) and then draws everything itself — text, shadows, buttons — using its own rendering engine (Skia historically, Impeller on iOS and increasingly on Android since Flutter 3.x). It never touches
UIVieworandroid.view.View. This buys pixel-perfect consistency across platforms at the cost of reimplementing platform look-and-feel by hand.
#Where each one actually loses time
Startup (time to first frame). Native wins almost by default — there's no runtime to boot. Flutter is close behind: the Dart AOT snapshot loads fast and the engine's init cost is on the order of tens of milliseconds. React Native pays for JS engine startup and bundle parsing; Hermes closed most of this gap by shipping precompiled bytecode instead of source, which removes the parse step, but a large, unoptimized JS bundle (or a slow chain of native module initializations firing on launch) can still show up clearly in a cold-start trace.
Scroll performance and jank. This is less about the framework than about whether the list is virtualized. A naive ScrollView mapping over 5,000 items will jank in any framework, including native UITableView used incorrectly. Given equivalent effort, native UICollectionView/RecyclerView and Flutter's ListView.builder (which recycles view objects and composites independently of application logic) tend to hold 60/120fps most reliably. React Native's FlatList, combined with the New Architecture's ability to commit layout on the native side without waiting on a busy JS thread, has closed most — not all — of the historical gap.
Threading model. Native gives you full control: GCD queues, Kotlin coroutines, whatever you want. Flutter runs your Dart code on a single UI isolate by default (isolates don't share memory, so parallelism means spinning up more isolates and passing messages, not shared-memory threading) — but compositing happens on a separate raster thread, so a busy UI isolate degrades interaction but doesn't necessarily drop already-scheduled frames outright. React Native keeps JS on its own thread; heavy synchronous JS work (a large JSON.parse, an unmemoized re-render cascade) blocks that thread, which is why offloading expensive work (image processing, crypto, heavy computation) into a native module or a background JS runtime is a standard mitigation.
Memory and binary size. Flutter ships the Dart runtime and the Skia/Impeller engine inside the app, typically adding several megabytes to binary size and some baseline memory overhead. React Native ships the Hermes engine (smaller than JSC or V8) plus bridge/JSI infrastructure. Native has neither of these fixed costs. On a memory-constrained low-end Android device, this baseline difference is often more perceptible to users than any of the above.
#The comparison, side by side
| Dimension | Native (Swift/Kotlin) | React Native (New Architecture) | Flutter |
|---|---|---|---|
| Rendering | Real platform widgets, no intermediate layer | Real platform widgets via Fabric + JSI | Self-drawn via Skia/Impeller, no platform widgets |
| Cold start | Fastest | Improved a lot by Hermes bytecode; still has JS init cost | Near-native, small AOT init overhead |
| Scroll/jank at scale | Best with correct list virtualization | Good with FlatList + proper memoization; historically the weak point | Very good, dedicated raster thread |
| Threading | Full native control (GCD, coroutines) | Single JS thread + native threads; JSI removes most serialization cost | Dart isolates; message-passing, not shared memory |
| Binary size overhead | None | + Hermes engine + JSI/bridge libs | + Dart runtime + Skia/Impeller |
| Baseline memory | Lowest | Moderate | Higher, engine-dependent |
| Platform look & feel | Automatic, always current | Automatic (real widgets pick it up) | Manual — Material/Cupertino widgets approximate it |
| Dev velocity | Slowest (two native codebases) | Fast — hot reload, single codebase | Fast — hot reload, single codebase |
#The uncomfortable truth: architecture beats framework choice
In practice, the single biggest performance lever on any of these stacks is not the framework — it's whether you virtualize your lists, avoid unnecessary re-renders/rebuilds, offload heavy work off the main/UI thread, and use the native animation driver (useNativeDriver/Reanimated worklets in RN, hardware-accelerated AnimatedBuilder in Flutter, Core Animation in native). A poorly built native app will lose to a well-built Flutter or React Native app on every one of these axes. When an interviewer pushes on "which is fastest," the strongest answer names the mechanism-level tradeoffs above and says that, in practice, engineering discipline within the chosen framework usually dominates the framework's own ceiling.
🧠 Check your understanding
0/2 · 0/2 answered1. What actually changed between React Native's old bridge and the New Architecture (JSI/Fabric) with respect to performance?
2. Why does Flutter tend to have a larger binary size and higher baseline memory than an equivalent native app?