Skip to main content

Documentation Index

Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt

Use this file to discover all available pages before exploring further.

React Native Architecture

Module Overview

Estimated Time: 2-3 hours | Difficulty: Beginner | Prerequisites: JavaScript, React basics
Before writing any code, it’s crucial to understand how React Native works under the hood. This knowledge will help you debug issues, optimize performance, and make better architectural decisions throughout your React Native journey. What You’ll Learn:
  • What React Native is and how it differs from other solutions
  • The JavaScript-to-Native bridge architecture
  • The new architecture (Fabric, TurboModules, JSI)
  • Hermes JavaScript engine
  • When to choose React Native for your project

What is React Native?

React Native is an open-source framework created by Meta (Facebook) in 2015 that allows developers to build truly native mobile applications using JavaScript and React. Unlike hybrid frameworks that use WebViews, React Native renders actual native UI components. Think of it like a universal translator: you write your instructions in JavaScript, and React Native translates them into the native language each platform speaks — Objective-C/Swift for iOS and Java/Kotlin for Android. The result is not a translation of a web page crammed into a phone, but a genuinely native app that uses the same UI building blocks as apps written in Xcode or Android Studio.

True Native

Renders to real native components (UIView, TextView), not web views

Cross-Platform

One codebase for iOS and Android with 90%+ code reuse

Hot Reloading

See changes instantly without rebuilding the entire app

JavaScript

Use the language and ecosystem you already know

Large Ecosystem

Access to npm packages and React libraries

Active Community

Backed by Meta with millions of developers worldwide

Who Uses React Native?

Meta

Facebook, Instagram, Messenger

Microsoft

Office, Outlook, Teams

Shopify

Shop app, Point of Sale

Discord

Mobile apps
Other notable companies: Coinbase, Pinterest, Bloomberg, Walmart, Tesla, and thousands more.

How React Native Works

The Rendering Pipeline

When you write React Native code, here’s what happens:
┌─────────────────────────────────────────────────────────────────────────────┐
│                    React Native Rendering Pipeline                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   1. Your React Code                                                         │
│      ┌──────────────────────────────────────────────────────────────────┐   │
│      │  const App = () => (                                              │   │
│      │    <View style={styles.container}>                                │   │
│      │      <Text>Hello, World!</Text>                                   │   │
│      │    </View>                                                        │   │
│      │  );                                                               │   │
│      └──────────────────────────────────────────────────────────────────┘   │
│                              │                                               │
│                              ▼                                               │
│   2. React Reconciliation                                                    │
│      ┌──────────────────────────────────────────────────────────────────┐   │
│      │  React creates a virtual DOM tree and calculates the minimal     │   │
│      │  set of changes needed (diffing algorithm)                       │   │
│      └──────────────────────────────────────────────────────────────────┘   │
│                              │                                               │
│                              ▼                                               │
│   3. Bridge / JSI Communication                                              │
│      ┌──────────────────────────────────────────────────────────────────┐   │
│      │  Changes are serialized and sent to the native side              │   │
│      │  (Legacy: JSON over bridge | New: Direct JSI calls)              │   │
│      └──────────────────────────────────────────────────────────────────┘   │
│                              │                                               │
│                              ▼                                               │
│   4. Native Rendering                                                        │
│      ┌──────────────────────────────────────────────────────────────────┐   │
│      │  iOS: UIView, UILabel, UIImageView                               │   │
│      │  Android: View, TextView, ImageView                              │   │
│      └──────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Component Mapping

React Native components map directly to native platform components:
React NativeiOS (UIKit)AndroidWeb Equivalent
<View>UIViewandroid.view.View<div>
<Text>UILabelTextView<p>, <span>
<Image>UIImageViewImageView<img>
<TextInput>UITextFieldEditText<input>
<ScrollView>UIScrollViewScrollView<div> with overflow
<FlatList>UITableViewRecyclerViewVirtual list

The Legacy Bridge Architecture

The original React Native architecture uses a “bridge” for communication between JavaScript and native code:
┌─────────────────────────────────────────────────────────────────────────────┐
│                      Legacy Bridge Architecture                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   JavaScript Thread                    Native Thread                         │
│   ─────────────────                    ─────────────                         │
│                                                                              │
│   ┌─────────────────┐                  ┌─────────────────┐                  │
│   │  Your React     │                  │  Native UI      │                  │
│   │  Components     │                  │  Components     │                  │
│   │                 │                  │                 │                  │
│   │  - App.tsx      │                  │  - UIView       │                  │
│   │  - Screens      │                  │  - TextView     │                  │
│   │  - Components   │                  │  - ImageView    │                  │
│   └────────┬────────┘                  └────────▲────────┘                  │
│            │                                    │                            │
│            │         ┌──────────────┐           │                            │
│            └────────►│    Bridge    │───────────┘                            │
│                      │              │                                        │
│                      │  - Async     │                                        │
│                      │  - JSON      │                                        │
│                      │  - Batched   │                                        │
│                      └──────────────┘                                        │
│                                                                              │
│   ┌─────────────────┐                  ┌─────────────────┐                  │
│   │  JS Engine      │                  │  Native Modules │                  │
│   │                 │                  │                 │                  │
│   │  - Hermes       │                  │  - Camera       │                  │
│   │  - JSC          │                  │  - GPS          │                  │
│   │                 │                  │  - Storage      │                  │
│   └─────────────────┘                  └─────────────────┘                  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

How the Bridge Works

Think of the bridge like sending letters between two people who speak different languages. Each message has to be written down (serialized to JSON), put in an envelope, delivered, translated, and then acted upon. This works, but it is slow when you need rapid back-and-forth communication — like coordinating a 60 FPS animation where every frame is a new letter.
  1. JavaScript Thread: Your React code runs here, handling business logic, state management, and UI declarations
  2. Bridge: Serializes messages to JSON and passes them asynchronously between threads
  3. Native Thread: Receives commands and renders actual native UI components
  4. Shadow Thread: Calculates layout using Yoga (Flexbox engine)

Bridge Limitations

The legacy bridge has several limitations that can impact performance:
  • Asynchronous Only: All communication is async, causing delays for time-sensitive operations
  • Serialization Overhead: Data must be serialized to JSON, adding CPU overhead
  • Single Bottleneck: All communication goes through one bridge
  • No Direct Access: JavaScript can’t directly call native methods
// Example: Bridge latency issue with animations
// This animation might feel janky because each frame
// requires a round-trip through the bridge (JS -> JSON -> Native -> render)

const opacity = useRef(new Animated.Value(0)).current;

Animated.timing(opacity, {
  toValue: 1,
  duration: 300,
  // useNativeDriver: true sends the entire animation description to the
  // native side once, so all 60 frames/sec are calculated on the UI thread
  // without crossing the bridge. This is critical for smooth animations.
  // Without it, each frame would require a JS-to-native round trip.
  useNativeDriver: true,
}).start();

The New Architecture

React Native’s new architecture (available since RN 0.68, default in 0.74+) addresses bridge limitations with three key components:
┌─────────────────────────────────────────────────────────────────────────────┐
│                         New Architecture Overview                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │                    JavaScript Interface (JSI)                        │   │
│   │   ─────────────────────────────────────────────────────────────────  │   │
│   │   • Direct C++ bindings between JS and Native                        │   │
│   │   • No serialization needed                                          │   │
│   │   • Synchronous calls possible                                       │   │
│   │   • Enables Fabric and TurboModules                                  │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│   ┌───────────────────────────┐    ┌───────────────────────────────────┐   │
│   │      Fabric Renderer      │    │         TurboModules              │   │
│   │   ─────────────────────   │    │         ────────────              │   │
│   │                           │    │                                   │   │
│   │   • New rendering system  │    │   • New native module system      │   │
│   │   • Synchronous layout    │    │   • Lazy loading                  │   │
│   │   • Concurrent features   │    │   • Type-safe (Codegen)           │   │
│   │   • Better performance    │    │   • Direct JS-Native calls        │   │
│   │                           │    │                                   │   │
│   └───────────────────────────┘    └───────────────────────────────────┘   │
│                                                                              │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │                          Codegen                                     │   │
│   │   ─────────────────────────────────────────────────────────────────  │   │
│   │   • Generates native code from TypeScript/Flow specs                 │   │
│   │   • Type-safe bridge between JS and Native                           │   │
│   │   • Compile-time validation                                          │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

JavaScript Interface (JSI)

JSI is the foundation of the new architecture — a lightweight C++ layer that allows JavaScript to hold references to native objects and call methods directly. If the old bridge was like sending letters, JSI is like the two people learning to speak the same language (C++) so they can have a real-time conversation with no translation delays:
// Conceptual example of JSI benefits
// Old Bridge: Async, serialized
bridge.call('NativeModule', 'getData', { id: 123 })
  .then(data => console.log(data));

// New JSI: Direct, can be sync
const data = NativeModule.getData({ id: 123 }); // Direct call!
JSI Benefits:
  • No Serialization: Objects are passed by reference, not serialized to JSON
  • Synchronous Calls: When needed, JS can call native code synchronously
  • Lazy Loading: Native modules load only when first accessed
  • Multiple Engines: JSI abstracts the JS engine, enabling Hermes, JSC, or V8

Fabric Renderer

Fabric is the new rendering system that replaces the old UI Manager:
┌─────────────────────────────────────────────────────────────────────────────┐
│                    Fabric vs Legacy Rendering                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   Legacy Rendering:                                                          │
│   ─────────────────                                                          │
│   JS Thread ──► Bridge ──► Shadow Thread ──► Bridge ──► UI Thread           │
│   (Async)       (JSON)     (Layout)          (JSON)     (Render)            │
│                                                                              │
│   Fabric Rendering:                                                          │
│   ─────────────────                                                          │
│   JS Thread ──► JSI ──► C++ Shadow Tree ──► UI Thread                       │
│   (Can be sync)  (Direct)  (Shared)          (Render)                       │
│                                                                              │
│   Benefits:                                                                  │
│   • Synchronous layout measurements                                          │
│   • Concurrent React features (Suspense, Transitions)                        │
│   • Better priority handling                                                 │
│   • Improved memory management                                               │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

TurboModules

TurboModules replace the old Native Modules system:
// Old Native Module (loaded at startup)
import { NativeModules } from 'react-native';
const { MyModule } = NativeModules; // Loaded immediately

// TurboModule (lazy loaded)
import { TurboModuleRegistry } from 'react-native';
const MyModule = TurboModuleRegistry.get('MyModule'); // Loaded on first access
TurboModule Benefits:
  • Lazy Loading: Modules load only when needed, improving startup time
  • Type Safety: Codegen creates type-safe interfaces from specs
  • Direct Calls: Uses JSI for direct native method invocation
  • Better Performance: No JSON serialization overhead

Codegen

Codegen generates native code from TypeScript/Flow specifications:
// NativeMyModule.ts - TypeScript spec
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  multiply(a: number, b: number): number;
  getString(): string;
  getObject(): { name: string; value: number };
}

export default TurboModuleRegistry.getEnforcing<Spec>('MyModule');

// Codegen generates:
// - iOS: Objective-C++ interface
// - Android: Java/Kotlin interface
// - Type-safe bindings

Hermes JavaScript Engine

Hermes is a JavaScript engine optimized specifically for React Native:
┌─────────────────────────────────────────────────────────────────────────────┐
│                    Hermes vs JavaScriptCore                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   JavaScriptCore (JSC):                                                      │
│   ─────────────────────                                                      │
│   Source Code ──► Parse ──► Compile (JIT) ──► Execute                       │
│                   (Runtime)  (Runtime)         (Runtime)                     │
│                                                                              │
│   Hermes:                                                                    │
│   ───────                                                                    │
│   Source Code ──► Parse ──► Compile ──► Bytecode ──► Execute                │
│                   (Build)    (Build)     (Ship)       (Runtime)             │
│                                                                              │
│   Hermes compiles JavaScript to bytecode at BUILD time,                     │
│   not at runtime. This means:                                                │
│   • Faster app startup (no parsing/compiling at launch)                     │
│   • Lower memory usage                                                       │
│   • Smaller app size (bytecode is smaller than source)                      │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Hermes Performance Benefits

MetricJavaScriptCoreHermesImprovement
Time to Interactive4.3s2.0s53% faster
Memory Usage185 MB136 MB26% less
App Size41 MB22 MB46% smaller
Benchmarks from Meta’s testing on a mid-range Android device The startup improvement is especially significant on Android, where device specs vary wildly. On a budget Android phone with 2-3 GB of RAM, the memory savings from Hermes can be the difference between your app running smoothly and the OS killing it in the background. On iOS, the improvements are present but less dramatic since Apple devices tend to have more consistent hardware specs.

Enabling Hermes

Hermes is enabled by default in new React Native projects (0.70+). To verify or enable:
// app.json (Expo)
{
  "expo": {
    "jsEngine": "hermes"
  }
}
# ios/Podfile (React Native CLI)
:hermes_enabled => true
// android/app/build.gradle (React Native CLI)
project.ext.react = [
    enableHermes: true
]

React Native vs Alternatives

Comparison Matrix

FeatureReact NativeNative (Swift/Kotlin)FlutterIonic/Cordova
LanguageJavaScript/TypeScriptSwift/KotlinDartHTML/CSS/JS
UI RenderingNative componentsNativeCustom (Skia)WebView
PerformanceNear-nativeBestNear-nativeSlower
Code Reuse90%+0%95%+95%+
Hot Reload✅ Yes❌ No✅ Yes✅ Yes
CommunityHugeLargestGrowing fastDeclining
Learning CurveEasy (if you know React)SteepMediumEasy
App SizeMedium (10-20MB)Small (5-10MB)Larger (15-25MB)Small
Native AccessVia bridge/JSIDirectVia channelsVia plugins
Web SupportReact Native WebN/AFlutter WebBuilt-in

When to Choose React Native

✅ Your team knows JavaScript/TypeScript and React✅ You need to ship iOS and Android apps quickly✅ You want near-native performance with code reuse✅ You need access to the npm ecosystem✅ You’re building a startup MVP or enterprise app✅ You want to share code with a React web app✅ You need over-the-air updates (CodePush/EAS Update)

Real-World Decision Framework

┌─────────────────────────────────────────────────────────────────────────────┐
│                    Technology Decision Framework                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   Question 1: Does your team know React/JavaScript?                          │
│   ├── Yes ──► Strong candidate for React Native                             │
│   └── No ──► Consider Flutter or Native                                     │
│                                                                              │
│   Question 2: Do you need to share code with web?                           │
│   ├── Yes ──► React Native + React Native Web                               │
│   └── No ──► Any option works                                               │
│                                                                              │
│   Question 3: Is performance absolutely critical?                           │
│   ├── Yes (games, AR) ──► Native or Flutter                                 │
│   └── No (most apps) ──► React Native is fine                               │
│                                                                              │
│   Question 4: Do you need OTA updates?                                      │
│   ├── Yes ──► React Native (CodePush/EAS Update)                            │
│   └── No ──► Any option works                                               │
│                                                                              │
│   Question 5: Timeline and budget?                                          │
│   ├── Fast & Limited ──► React Native or Flutter                            │
│   └── Flexible ──► Any option works                                         │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

React Native Ecosystem

Core Tools

Expo

Managed workflow with pre-built native modules, OTA updates, and cloud builds

React Navigation

The standard navigation library for React Native apps

React Native CLI

Bare workflow with full native code access

Metro

JavaScript bundler optimized for React Native
CategoryLibraryPurpose
StateRedux Toolkit, Zustand, JotaiGlobal state management
Data FetchingTanStack Query, SWRServer state and caching
FormsReact Hook Form, FormikForm handling and validation
UIReact Native Paper, TamaguiComponent libraries
AnimationReanimated, MotiHigh-performance animations
StorageMMKV, AsyncStorageLocal data persistence
TestingJest, DetoxUnit and E2E testing

Understanding the Threads

React Native runs on multiple threads:
┌─────────────────────────────────────────────────────────────────────────────┐
│                         React Native Threads                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   1. JavaScript Thread                                                       │
│      ─────────────────                                                       │
│      • Runs your React code                                                  │
│      • Handles business logic                                                │
│      • Manages state and props                                               │
│      • Single-threaded (one JS thread)                                       │
│                                                                              │
│   2. Main/UI Thread                                                          │
│      ─────────────────                                                       │
│      • Renders native UI                                                     │
│      • Handles touch events                                                  │
│      • Must stay responsive (60 FPS = 16ms per frame)                       │
│      • Platform's main thread                                                │
│                                                                              │
│   3. Shadow Thread (Legacy) / C++ Thread (New Arch)                         │
│      ─────────────────────────────────────────────────                       │
│      • Calculates layout using Yoga                                          │
│      • Determines component positions and sizes                              │
│      • Runs Flexbox calculations                                             │
│                                                                              │
│   4. Native Modules Thread                                                   │
│      ─────────────────────                                                   │
│      • Runs native module code                                               │
│      • Handles async operations                                              │
│      • Camera, GPS, file system, etc.                                        │
│                                                                              │
│   Performance Tip:                                                           │
│   Keep the JS thread free for UI updates. Heavy computations                │
│   should be moved to native modules or web workers.                         │
│                                                                              │
│   Practical Pitfall:                                                         │
│   If your JS thread is busy (e.g., parsing large JSON), touch               │
│   events will still be detected by the native UI thread, but the            │
│   JS callback won't fire until the JS thread is free. This is why           │
│   buttons can feel "unresponsive" during heavy computation.                  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Thread Communication Example

// This runs on JS thread
const handlePress = () => {
  // This computation blocks the JS thread
  const result = heavyComputation(); // ❌ Bad - blocks UI updates
  
  // Better: Use native module or InteractionManager
  InteractionManager.runAfterInteractions(() => {
    const result = heavyComputation(); // ✅ Runs after animations complete
  });
};

// Even better: Use useNativeDriver for animations
Animated.timing(opacity, {
  toValue: 1,
  duration: 300,
  useNativeDriver: true, // ✅ Animation runs on UI thread, not JS thread
}).start();

Key Takeaways

React Native Renders Native

Unlike hybrid apps, React Native renders actual native UI components

Bridge is Being Replaced

The new architecture (JSI, Fabric, TurboModules) eliminates bridge bottlenecks

Hermes Improves Performance

Hermes compiles JS to bytecode at build time for faster startup

Choose Based on Team

React Native is ideal if your team knows React/JavaScript

Practice Exercise

1

Research

Look up 3 apps you use daily and check if they’re built with React Native (hint: check their job postings or tech blogs)
2

Compare

Download the same app on iOS and Android. Notice any differences in UI or behavior? React Native apps should feel native on each platform.
3

Explore

Visit the React Native Directory and browse popular libraries. Note which categories have the most options.

Quiz

Answer: React Native renders to actual native UI components (UIView, TextView), while hybrid frameworks render to WebViews. This gives React Native near-native performance and platform-specific look and feel.
Answer:
  1. JSI (JavaScript Interface): Direct C++ bindings between JS and native
  2. Fabric: New rendering system with synchronous layout
  3. TurboModules: Lazy-loaded, type-safe native modules
Answer: Hermes compiles JavaScript to bytecode at build time (ahead-of-time compilation), while JSC compiles at runtime. This means Hermes apps start faster because they skip parsing and compilation at launch.
Answer: Avoid React Native for:
  • Performance-critical apps (games, AR/VR)
  • Apps requiring immediate access to cutting-edge platform features
  • When app size is critical (RN adds ~10MB)
  • When you have dedicated native teams

Next Steps

Module 2: Environment Setup

Set up your development environment for iOS and Android development

Interview Deep-Dive

Strong Answer:
  • Old bridge path: The user’s tap is detected by the native UI thread, which serializes the touch event into a JSON message and sends it across the bridge to the JS thread. The JS thread processes the event (running your onPress handler), updates React state, triggers reconciliation, computes the minimal set of UI changes, serializes those changes to JSON, and sends them back across the bridge to the native thread, which applies the updates and re-renders the affected native views. Total: two bridge crossings, two JSON serialization/deserialization cycles.
  • New architecture (JSI) path: The tap event can be dispatched directly to the JS thread via JSI’s C++ bindings — no JSON serialization. The JS handler runs, React reconciles, and the resulting UI mutations are applied directly to the shared C++ shadow tree that both JS and native threads can access. The native renderer picks up changes from the shadow tree and commits them to the screen. Total: zero serialization, potentially synchronous for layout measurements.
  • The practical difference becomes visible in gesture-heavy interactions: a drag-to-reorder list on the old bridge feels subtly laggy because each drag position update requires a full bridge round-trip. On JSI with Reanimated, the gesture handler runs entirely on the UI thread via worklets, only notifying JS when the gesture completes. The result is 60fps drag interactions even on mid-range devices.
Follow-up: How does useNativeDriver: true on the Animated API bypass the bridge, and why can it only animate certain properties?Follow-up Answer:
  • When you set useNativeDriver: true, the Animated API serializes the entire animation description (start value, end value, duration, easing function) into a single message and sends it to the native side once at the start. The native animation driver then runs all frames on the UI thread without any further JS involvement. This is why it can maintain 60fps even when the JS thread is busy.
  • The limitation is that the native animation driver can only operate on properties that exist in the native view’s layout system and can be updated without triggering a full layout recalculation. Transform properties (translateX, translateY, scale, rotate) and opacity work because they are composited by the GPU without affecting surrounding views. Properties like width, height, margin, and backgroundColor do not work with useNativeDriver because changing them requires Yoga to recalculate layout for the entire subtree, which must happen on the JS/shadow thread.
  • This is exactly why Reanimated exists — it provides its own worklet-based animation system that runs on the UI thread and can animate any property, including layout properties, by performing layout calculations natively.
Strong Answer:
  • Hermes uses Ahead-of-Time (AOT) compilation: JavaScript is parsed, compiled to bytecode, and optimized at build time. At runtime, the Hermes VM interprets this bytecode directly. V8 (and JavaScriptCore to a lesser extent) use Just-in-Time (JIT) compilation: they start interpreting, identify hot code paths at runtime, and compile those paths to optimized machine code on the fly.
  • Hermes wins on startup time (no parse/compile phase at launch), memory usage (bytecode is more compact than source + AST, and no JIT compiler running in memory), and app size (bytecode is smaller than minified JavaScript). These are exactly the metrics that matter most on mobile.
  • Hermes can be slower for long-running CPU-intensive computations. A JIT compiler like V8 can optimize hot loops into near-native-speed machine code, while Hermes’s interpreter runs bytecode with a fixed overhead per instruction. In practice, this matters for: heavy data processing (sorting 100K records client-side), cryptographic operations in JS, and complex regex matching on large strings.
  • The pragmatic response: these scenarios should not be happening in JS on mobile anyway. Heavy computation should be offloaded to native modules or performed server-side. For the 99% of mobile app code that is UI rendering, event handling, and API calls, Hermes’s startup and memory advantages far outweigh the JIT speed advantage on hot loops.
Follow-up: You notice your Hermes-powered app has a 3-second cold start time on a mid-range Android device. Walk me through how you would diagnose and reduce this.Follow-up Answer:
  • First, measure what is actually happening during those 3 seconds. Use performance.now() markers at key points: native app launch, JS bundle load start/end, first React render, and time-to-interactive. Hermes provides runtime properties via global.HermesInternal.getRuntimeProperties() that tell you bytecode load time and heap size.
  • Common culprits: too many native modules loading eagerly at startup (migrate to TurboModules for lazy loading), large JS bundle size (enable Metro’s tree shaking, audit your imports for accidentally bundling entire libraries), expensive top-level code that runs during module evaluation (move initialization into lazy useEffect calls), and synchronous storage reads at startup blocking the render.
  • Practical fixes in priority order: (1) enable Hermes if not already enabled, (2) use require calls inside functions instead of top-level imports for non-critical modules, (3) defer non-essential initialization to after the first frame using InteractionManager.runAfterInteractions(), (4) show a native splash screen during JS initialization so the user sees something immediately, (5) audit and reduce the number of providers wrapping your app root — each context provider is a React component that renders during the critical path.
Strong Answer:
  • Scenario 1: A game studio building a 3D mobile game with physics simulation. React Native’s rendering pipeline is designed for form-based UI components, not GPU-intensive rendering. The JS-to-native abstraction adds overhead that is unacceptable when you need to render 10,000 particles at 60fps. Use Unity, Unreal, or native Metal/Vulkan.
  • Scenario 2: A company with established, mature iOS and Android teams who already have a shared C++ core library. Adding React Native introduces a third language (JavaScript), a third build system (Metro), and a third paradigm (React) without retiring the existing ones. The coordination cost outweighs the code-sharing benefit. Better to invest in KMM (Kotlin Multiplatform Mobile) for shared business logic while keeping native UI.
  • Scenario 3: An app that needs same-day access to a brand-new platform API (like a new iOS widget type released at WWDC). React Native and its community libraries lag behind native SDKs by weeks to months. If your competitive advantage depends on being first to adopt new platform features, native gives you that capability.
  • Scenario 4: A hardware companion app that requires constant Bluetooth Low Energy communication with a custom protocol. BLE in React Native requires native modules, and the async bridge (even with JSI) introduces latency that can break timing-sensitive BLE protocols. The entire core functionality would end up in native code, making React Native just an expensive wrapper around native screens.
  • The pattern: React Native excels at content-driven, form-heavy, CRUD-style apps (which is 80% of all apps). It struggles when the core value proposition requires sustained, low-latency access to hardware or GPU.
Follow-up: Your team decided to use React Native, but 6 months in you discover that a critical feature requires native-level performance. What is your recovery plan?Follow-up Answer:
  • Do not panic and do not rewrite. React Native’s architecture explicitly supports this scenario through native modules. Identify the specific feature that needs native performance, define a clear API boundary (what data goes in, what comes out), and implement it as a TurboModule.
  • For example, if the feature is real-time video processing, the native module handles camera capture and frame processing in Swift/Kotlin, exposes a processFrame() function via JSI, and returns results to the JS layer. The other 50 screens in your app remain React Native and benefit from the cross-platform code sharing.
  • The key architectural decision is the API boundary. Design it as a thin, stable interface that decouples the native implementation from the JS consumer. Use TypeScript specs with Codegen so the contract is enforced at compile time. This way, the native team can optimize the module’s internals without breaking the React Native screens that consume it.