AboutBlogContact
Mobile DevelopmentSeptember 14, 2025 7 min read 493Updated: May 18, 2026

React Native New Architecture in Practice: JSI, Fabric, and TurboModules Explained

AunimedaAunimeda
📋 Table of Contents

React Native New Architecture in Practice: JSI, Fabric, and TurboModules Explained

React Native's architecture was fundamentally unchanged from 2015 to 2022. The Bridge - an asynchronous JSON-serialized message bus between JavaScript and native code - worked well enough for most apps, but had hard limits that couldn't be engineered around.

In 2022, Meta started shipping the New Architecture. In React Native 0.74 (2024), it became opt-in stable. In 0.76, it became the default for new projects.

Here's what actually changed, why it matters, and what you need to know to use it effectively.


What was wrong with the old Bridge

The old architecture had a single, asynchronous Bridge between JavaScript and the native layer. Every interaction - calling a native module, updating UI, responding to events - went through this Bridge as serialized JSON strings.

JavaScript (V8/Hermes) ←→ Bridge (JSON serialization) ←→ Native (Swift/Kotlin)

Problems this created:

Asynchronous by default. Calling a native module and getting a result required at least two round trips through the bridge. For time-sensitive operations (touch response, gesture handling), this 1-3ms overhead was noticeable.

JSON serialization cost. Every value crossing the bridge - numbers, strings, objects, arrays - was serialized to JSON on one side and deserialized on the other. For high-frequency data (camera frames, gyroscope readings, scroll position), this was significant CPU overhead.

Single-threaded UI updates. All layout calculations happened on a dedicated native thread, but layout could not be triggered synchronously from JavaScript. Animated gestures would lag because JavaScript couldn't respond synchronously to layout changes.

Startup cost. All native modules had to be initialized at startup, whether used or not, because the bridge needed a complete module registry.


JSI: JavaScript Interface

JSI (JavaScript Interface) replaces the Bridge with a direct C++ binding to the JavaScript engine.

JavaScript (Hermes) ←→ JSI (C++ host objects) ←→ Native (Swift/Kotlin)

The critical change: JavaScript can now hold references to native objects and call native functions synchronously. No serialization. No round trip. Direct function call.

// Old Bridge: JS calls native module
// JS side: NativeModules.Camera.capture(options, callback)
// Serializes to JSON, posts to native thread, native executes, 
// posts result back to JS thread via callback

// JSI: JS holds a reference to a C++ object
// JS side: global.__camera.capture(options) - synchronous, direct call
// No serialization, no thread hop for the call itself

In practice, this means:

  • Native modules can expose values that JavaScript can read without any async overhead
  • JavaScript can call native functions and get synchronous return values when needed
  • Typed data (TypedArrays like Float32Array) can be shared between JS and native without copying - critical for camera, audio, ML

TurboModules

TurboModules use JSI to make native modules:

  1. Lazily initialized - only loaded when first accessed, not at startup
  2. Strongly typed - type spec is defined in TypeScript, C++ code generated from it
  3. Synchronous-capable - can return values synchronously via JSI when needed
// TurboModule spec (TypeScript → generates C++ bridge code)
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  // Synchronous - returns value directly
  getDeviceId(): string;
  
  // Async - still Promise for genuinely async operations
  getLocationPermission(): Promise<'granted' | 'denied' | 'never_ask_again'>;
  
  // Callback-based
  startAccelerometer(interval: number, callback: (data: AccelerometerData) => void): void;
  stopAccelerometer(): void;
}

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

The startup benefit is real. A large React Native app with 30-40 native modules in the old architecture would initialize all of them at launch. With TurboModules, modules are initialized on first use. In our measurements: 400-700ms startup time reduction for apps with heavy native module usage.


Fabric: the new renderer

Fabric is the new UI rendering engine. It replaces the old UIManager with a C++ core that:

  1. Runs layout synchronously on both the JS thread and native thread
  2. Supports concurrent features - React 18's Suspense, transitions, and concurrent rendering
  3. Enables synchronous native events - touch events can now be handled without async roundtrips

The old renderer calculated layout on a separate "shadow thread" then applied to native views. The new renderer keeps a shadow tree in C++ that mirrors the React tree, enabling direct synchronous communication.

// Old architecture: this animation could stutter
// Because Animated had to cross the bridge on each frame
const opacity = new Animated.Value(1);

// The new Animated (Reanimated 3 with New Architecture)
// runs entirely on the UI thread - zero bridge overhead
import Animated, { useSharedValue, withSpring } from 'react-native-reanimated';

function MyComponent() {
  const scale = useSharedValue(1);
  
  // This worklet runs on UI thread, not JS thread
  // No bridge, no lag, 60/120fps guaranteed
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));
  
  return (
    <Animated.View style={animatedStyle}>
      <Text>60fps, always</Text>
    </Animated.View>
  );
}

Real performance differences

From a production app migration (e-commerce, ~120k MAU):

Metric Old Architecture New Architecture Change
Cold start (iOS) 2,340ms 1,680ms -28%
Cold start (Android) 3,100ms 2,200ms -29%
Scroll FPS (product list) 52fps avg 59fps avg +13%
Gesture response latency 18ms 4ms -78%
Memory at startup 187MB 164MB -12%
JS bundle parse time 340ms 210ms -38%

The gesture response latency improvement is the most user-visible. Tapping a button and seeing immediate visual feedback is qualitatively different even at these small absolute numbers.


Migration checklist

If you're migrating an existing app:

# 1. Update to RN 0.74+ (New Architecture opt-in) or 0.76+ (default)
npx react-native upgrade

# 2. Enable New Architecture (if on 0.74/0.75)
# android/gradle.properties
newArchEnabled=true

# iOS: Podfile
ENV['RCT_NEW_ARCH_ENABLED'] = '1'

Native module compatibility: Any native module using the old Bridge API will break. Check whether your dependencies have TurboModule support:

# Check for New Architecture support in your dependencies
npx react-native-community/directory search --filters="new-architecture"

Major libraries with New Architecture support as of 2025:

  • react-native-reanimated 3.x ✅
  • react-native-gesture-handler 2.x ✅
  • react-native-screens 3.x ✅
  • react-native-camera - use react-native-vision-camera 4.x instead ✅
  • react-native-maps 1.10+ ✅
  • react-native-firebase 21+ ✅

The interop layer: If you have old-architecture native modules that don't have TurboModule versions, the interop layer lets them still work. Enable it in android/app/src/main/jni/MainApplicationModuleProvider.cpp. This means you can migrate incrementally.


When New Architecture matters most

High-gain scenarios:

  • Apps with heavy gesture interactions (drag, swipe, pinch-zoom)
  • Camera and media processing
  • Real-time data visualization (charts, maps)
  • Large lists with complex item layouts
  • Apps with many native modules (telemetry, analytics, payments, maps all add up)

Lower impact:

  • Simple form-based apps
  • Apps with mostly static content
  • Apps where network latency dominates user experience

If your app's main bottleneck is API response time, New Architecture won't move your key metrics. Profile first. The 28% startup improvement is real and universal, but the rest depends heavily on what your app does.


The JSI-native module pattern

Writing a TurboModule from scratch for a custom native feature:

// specs/NativeImageProcessor.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  processImage(
    imagePath: string,
    operations: ReadonlyArray<{
      type: 'resize' | 'compress' | 'crop';
      params: Object;
    }>
  ): Promise<string>; // Returns path to processed image
  
  // Synchronous - safe for simple operations
  getImageDimensions(imagePath: string): { width: number; height: number };
}

export default TurboModuleRegistry.getEnforcing<Spec>('ImageProcessor');
// iOS: ImageProcessorModule.swift
@objc(ImageProcessor)
class ImageProcessorModule: NSObject, NativeImageProcessorSpec {
  
  // Synchronous - JSI calls this directly on calling thread
  func getImageDimensions(_ imagePath: String) -> [String: NSNumber] {
    guard let image = UIImage(contentsOfFile: imagePath) else {
      return ["width": 0, "height": 0]
    }
    return [
      "width": NSNumber(value: Float(image.size.width)),
      "height": NSNumber(value: Float(image.size.height)),
    ]
  }
  
  // Async
  func processImage(_ imagePath: String, operations: [[String: Any]], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
    DispatchQueue.global(qos: .userInitiated).async {
      // Process image...
      resolve(outputPath)
    }
  }
}

The New Architecture is no longer a preview or a beta. If you're starting a new React Native project in 2025 or 2026, you're using it by default. If you have a production app on the old architecture, the migration is worth doing - the startup improvement alone justifies the work for most apps.


Aunimeda develops mobile applications for iOS and Android - from MVP to production-ready apps with full backend integration.

Contact us to discuss your mobile project. See also: Mobile App Development, Mobile Game Development

Read Also

Flutter vs React Native in 2026: Which Should You Choose?aunimeda
Mobile Development

Flutter vs React Native in 2026: Which Should You Choose?

Flutter or React Native for your mobile app in 2026? A practical comparison of performance, ecosystem, developer experience, and which framework wins for different project types.

Flutter App Development in Bishkek: Cost, Timeline, and What to Expectaunimeda
Mobile Development

Flutter App Development in Bishkek: Cost, Timeline, and What to Expect

Everything you need to know about Flutter mobile app development in Bishkek, Kyrgyzstan in 2026: costs, timelines, team structure, and how to evaluate a development partner.

React Native Push Notifications in 2026: Complete Guide (Expo + Firebase)aunimeda
Mobile Development

React Native Push Notifications in 2026: Complete Guide (Expo + Firebase)

Push notifications are the single highest-ROI feature in mobile apps. Open rates are 7x higher than email. Here's how to implement them correctly in React Native - including background handling, deep linking, and analytics.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Mobile App Development

Get Consultation All articles