Cross-Platform Comparison
Choosing between Flutter and React Native — rendering model, perf, team fit, native interop, ecosystem
Cross-Platform Comparison
Choosing a cross-platform framework is mostly a fit decision, not a "which is better" decision. Both Flutter and React Native ship apps at scale. The selection turns on existing skills, native-interop needs, design fidelity targets, and team size.
The Honest Short Version
| Question | Pick Flutter | Pick React Native |
|---|---|---|
| Team comes from React/web? | ✅ | |
| Pixel-identical UI across platforms is a hard requirement? | ✅ | |
| Need to embed many native UI components? | ✅ | |
| Need consistent, high-fidelity custom animation across platforms? | ✅ | |
| Need to share code with a web app? | Possible, with caveats | ✅ (RN + RN Web or RN + Next sharing logic) |
| Native interop is critical (lots of platform SDKs)? | Workable | ✅ |
| Want strongest typing out of the box? | ✅ (Dart) | ✅ (TS) — both fine |
If the team's React skills are real and the app is mainly forms + lists + APIs, React Native is usually the lower-friction choice. If the app is highly designed, gesture-heavy, animation-heavy, or needs identical pixel output on Android and iOS, Flutter has the structural edge.
Rendering Model
This is the single biggest architectural difference.
Flutter
- Owns the canvas. Flutter renders every pixel itself via Skia (or Impeller on iOS).
- Widgets are not platform views; they're descriptions consumed by a custom renderer.
- Pro: identical visuals across platforms, full control of every frame.
- Pro: no JS/native bridge cost — Dart compiles to native AOT.
- Con: native UI conventions (system fonts, native context menus, accessibility) must be re-implemented.
- Con: embedding platform views (
AndroidView,UiKitView) is comparatively expensive.
React Native
- Uses platform views. Each component maps to a real
UIView(iOS) orView(Android). - The new Fabric renderer brings a concurrent-rendering–aware model, but the leaves are still native views.
- Pro: looks and behaves natively for free; accessibility plumbing via platform.
- Pro: embedding existing native UI is straightforward.
- Con: maintaining identical pixel output across platforms takes more effort.
- Con: high-frequency UI updates historically pay bridge cost (mitigated by JSI, Reanimated, Fabric).
Language
| Aspect | Flutter (Dart) | React Native (TS/JS) |
|---|---|---|
| Compile output | AOT native (release), JIT (debug) | JS bytecode via Hermes |
| Null safety | Sound, language-level | Sound via TypeScript strict |
| Ecosystem size | Smaller than JS, focused | Largest in the world (npm) |
| Hiring pool | Smaller but growing | Vast (any React dev) |
| Hot reload | Excellent, with state preserved | Fast Refresh (component state preserved) |
| Concurrent / async model | Isolates + async/await | Single JS thread + workers + Reanimated UI thread |
Dart's tooling is more uniform and easier to onboard. The JS ecosystem is bigger, but inconsistent — you choose between many state libraries, bundlers, and patterns.
Performance Profile
Both can hit 60 fps on mid-range hardware when used correctly. They fail in different ways:
| Failure mode | Flutter | React Native |
|---|---|---|
| Rebuild blowup | Whole subtree rebuilds without const or Selector | Whole tree re-renders without memo/selector |
| Animation jank | Rare — runs on the UI thread by default | If you skip useNativeDriver / Reanimated |
| Long lists | ListView.builder virtualization | FlatList (config-heavy) or FlashList |
| Cold start | Slower app size grows the binary | Hermes makes parse fast; native init dominates |
| Heavy CPU work | Move to Isolate | Move to a worker (web-worker-like) or native module |
The framework alone does not make an app fast or slow — discipline does.
Native Interop
| Concern | Flutter | React Native |
|---|---|---|
| Sync method call | MethodChannel (async only) or FFI (sync, C) | TurboModule (sync supported) or JSI |
| Native UI embedding | AndroidView / UiKitView (perf cost) | First-class — components are native views |
| Codegen | Pigeon (optional) | Codegen (default in New Architecture) |
| FFI to C/C++ | First-class dart:ffi | Possible via JSI |
| Plugin authoring | Pub package + platform code | npm package + platform code |
If the app must surface many native SDKs (mapping, payments, telco, vendor identity), React Native usually has less friction. If you need fast sync access to C libraries, Flutter's FFI is excellent.
Code Sharing With Web
| Path | Flutter | React Native |
|---|---|---|
| One codebase to web | Flutter Web (works, not pixel-perfect for content sites) | RN Web (same components on web, mature) |
| Share logic with a separate Next.js app | Painful (Dart ↔ TS) | Trivial (TS everywhere) |
| Share design system | Re-implement in Flutter widgets | Share with a react-native-web package |
If a web product is part of the strategy, React Native wins on logic sharing. Flutter Web is acceptable for app-like portals, less so for SEO-critical content sites.
Build, Deploy, OTA Updates
| Concern | Flutter | React Native |
|---|---|---|
| Build tooling | flutter build, single command | Gradle (Android) + Xcode (iOS) — more moving parts |
| App size baseline | ~5 MB engine + assets | ~10 MB engine + Hermes + JS bundle |
| Over-the-air JS update | Not supported (binary AOT) | CodePush, EAS Update, custom — JS bundle swappable |
| Compliance with store rules | Standard | Be careful with OTA scope — Apple allows JS code updates only |
OTA updates are a significant operational lever for React Native: ship fixes within hours, not through store review. Flutter cannot do this for Dart code.
Team & Hiring
| Reality | Implication |
|---|---|
| Web React developers outnumber Flutter developers | RN ramps a new hire faster if they have React experience |
| Flutter developers cluster — fewer but more focused | Mature Flutter shops report tighter conventions |
| Designer collaboration | Flutter widgets are bespoke; designers tooling (Figma → Flutter) is improving but not standard. RN component libraries map naturally to native UI familiar to designers. |
Ecosystem Snapshot (Practical)
| Need | Flutter package | React Native package |
|---|---|---|
| Navigation | go_router | @react-navigation/native |
| State management | riverpod / bloc | zustand / @reduxjs/toolkit / TanStack Query |
| Server state cache | manual / flutter_riverpod async | @tanstack/react-query |
| Forms | reactive_forms / flutter_form_builder | react-hook-form + zod |
| Local DB | drift / isar / hive | op-sqlite / WatermelonDB / react-native-mmkv |
| Networking | dio / chopper | fetch + interceptor / axios |
| Crash reporting | Sentry / Firebase Crashlytics | Sentry / Firebase Crashlytics |
| Push | firebase_messaging | @react-native-firebase/messaging / Notifee |
| Animations | Implicit + explicit + Rive | react-native-reanimated v3 |
Both ecosystems cover the typical app. RN edges out for telco-specific SDKs (more vendors ship Android/iOS-only SDKs and an RN wrapper than Dart bindings).
Migration Reality
Migrating an existing native app:
- Brownfield is supported in both. Flutter has
add-to-app, RN has standard hosting inUIViewController/Activity. - Maintenance teams report RN brownfield is smoother because the leaves are still native — less impedance with existing screens.
- Greenfield: both excellent.
Decision Framework
Start with the team and the goal.
1. Existing React/web team that values code reuse with web?
→ React Native
2. Brand-led product, identical visuals everywhere, lots of custom animation?
→ Flutter
3. Many native SDKs, telco/finance/vendor integrations?
→ React Native (lower interop cost)
4. Tight perf budget, no JS to manage, full rendering control?
→ Flutter
5. Need OTA updates to ship fixes hourly?
→ React Native (CodePush / EAS Update)
6. Hiring is a real constraint?
→ Whichever your local market supplies more of (usually React Native)Anti-Patterns Either Way
- Choosing by hype, not fit. Both are mature; the answer changes per team.
- Mixing both in one app. It's possible (separate modules), but doubles the operational burden — only do it as a migration step.
- Underestimating designer friction. Flutter's bespoke widgets need a tighter design partnership; without it, apps look "Flutter-ish" rather than native.
- Underestimating RN performance discipline. RN goes fast only when you respect bridge cost, list virtualization, and re-render scope.
TL;DR
- React Native is the safer default for a web-heavy organization or one with many native SDK dependencies.
- Flutter is the safer default when pixel-perfect cross-platform design and animation are core to the product.
- Both ship apps at scale; the failure mode is undisciplined engineering, not the framework choice.