DEV Community

anubhuti chopra
anubhuti chopra

Posted on

2 1 1 1 1

React Native’s New Architecture: A Beginner-Friendly Guide to Faster, Smoother Apps

React Native’s new architecture is a game-changer for mobile developers, offering a massive boost in performance, flexibility, and scalability. This shift is centered around three core technologies – Fabric, TurboModules, and JSI (JavaScript Interface) – which together provide a more seamless bridge between JavaScript and native code.

Why a New Architecture?

The old React Native bridge worked well, but it had some limitations:

  • Performance Issues: Communication between JavaScript and native code was slow and asynchronous.
  • High Memory Usage: The old bridge duplicated data, increasing memory usage.
  • Complex Debugging: It was hard to track bugs across the JS-native boundary.

The new architecture fixes these problems, providing a more responsive and efficient framework.

Key Components of the New Architecture

1. Fabric – The New Rendering System (Example: High-Performance UI Updates)

To understand how Fabric can make your UI more responsive, here’s a simple example of a component using React’s concurrent mode to handle intensive state updates efficiently.

import React, { useState, useTransition } from 'react';
import { View, Button, Text, StyleSheet } from 'react-native';

export default function App() {
    const [isPending, startTransition] = useTransition();
    const [count, setCount] = useState(0);

    const handleHeavyUpdate = () => {
        startTransition(() => {
            for (let i = 0; i < 5000; i++) {
                setCount((prev) => prev + 1);
            }
        });
    };

    return (
        <View style={styles.container}>
            <Text style={styles.text}>Count: {count}</Text>
            <Button title="Perform Heavy Update" onPress={handleHeavyUpdate} />
            {isPending && <Text>Updating...</Text>}
        </View>
    );
}

const styles = StyleSheet.create({
    container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
    text: { fontSize: 24, marginBottom: 20 }
});
Enter fullscreen mode Exit fullscreen mode

With Fabric, this component can handle frequent updates without lag, providing a smoother experience for your users.

Fabric is the new engine that drives how UI components are rendered. It’s faster because it talks to the native layer more directly.

  • Better Performance: Fabric uses React’s concurrent mode for faster updates.
  • Real-Time Layout: UI components respond immediately without waiting for the bridge.
  • Simpler Code: Easier to build complex animations and gestures.

2. TurboModules – Faster, More Flexible Native Modules (Example: Native Module Integration)

Here’s a simple example of creating a TurboModule for a native function. Suppose you want to expose a native method that returns the battery level on Android.

Create a Native Module (Java)

// BatteryModule.java
package com.yourapp;
import android.os.BatteryManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

public class BatteryModule extends ReactContextBaseJavaModule {
    BatteryModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "BatteryModule";
    }

    @ReactMethod
    public void getBatteryLevel(Promise promise) {
        try {
            IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
            Intent batteryStatus = getReactApplicationContext().registerReceiver(null, ifilter);
            int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
            promise.resolve(level);
        } catch (Exception e) {
            promise.reject("Error", e);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Use the Native Module (JavaScript)

import { NativeModules, Button, Text, View } from 'react-native';
import React, { useState } from 'react';

export default function App() {
    const [batteryLevel, setBatteryLevel] = useState(null);

    const fetchBatteryLevel = async () => {
        try {
            const level = await NativeModules.BatteryModule.getBatteryLevel();
            setBatteryLevel(level);
        } catch (error) {
            console.error(error);
        }
    };

    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <Text>Battery Level: {batteryLevel !== null ? batteryLevel + '%' : 'Unknown'}</Text>
            <Button title="Get Battery Level" onPress={fetchBatteryLevel} />
        </View>
    );
}
Enter fullscreen mode Exit fullscreen mode

This demonstrates how to expose native functionality using TurboModules for faster, more direct communication.

TurboModules replace the old way of connecting to native modules. They are faster because they avoid the slow, serialized bridge communication.

  • On-Demand Loading: Load only what you need, when you need it.
  • Direct Access: No more slow JSON conversion – it’s all direct communication.
  • Smaller App Sizes: Load fewer modules, save memory.

3. JSI (JavaScript Interface) – The Backbone (Example: Direct Native Access)

JSI allows you to directly call native functions from JavaScript without a bridge. Here’s a simple example of using JSI to add two numbers in C++.

Create a JSI Module (C++)

// MyJSIModule.cpp
#include <jsi/jsi.h>
#include <react-native/jsi/jsilib.h>

using namespace facebook::jsi;

void install(Runtime &jsiRuntime) {
    auto addNumbers = Function::createFromHostFunction(
        jsiRuntime,
        PropNameID::forAscii(jsiRuntime, "addNumbers"),
        2,
        [](Runtime &rt, const Value &, const Value *args, size_t) -> Value {
            double a = args[0].asNumber();
            double b = args[1].asNumber();
            return Value(a + b);
        }
    );
    jsiRuntime.global().setProperty(jsiRuntime, "addNumbers", std::move(addNumbers));
}
Enter fullscreen mode Exit fullscreen mode

Use the JSI Function (JavaScript)

import React, { useEffect, useState } from 'react';
import { Text, View, Button } from 'react-native';

export default function App() {
    const [result, setResult] = useState(0);

    useEffect(() => {
        const sum = global.addNumbers(10, 20);
        setResult(sum);
    }, []);

    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <Text>Result: {result}</Text>
            <Button title="Calculate 10 + 20" onPress={() => setResult(global.addNumbers(10, 20))} />
        </View>
    );
}
Enter fullscreen mode Exit fullscreen mode

This code directly calls the C++ function from JavaScript, providing near-instant performance.

JSI is the heart of the new architecture. It directly connects JavaScript to native code, removing the old bridge entirely.

  • Faster Calls: Instant communication with native code.
  • Better Memory Management: Share data without copying it.
  • Cross-Engine Support: Use different JS engines like V8 or Hermes.

How to Migrate to the New Architecture

Step 1: Prepare Your Project

First, make sure you’re using React Native 0.72+:

npx react-native upgrade
Enter fullscreen mode Exit fullscreen mode

Step 2: Enable the New Architecture

Update your android/gradle.properties to enable the new architecture:

newArchEnabled=true
Enter fullscreen mode Exit fullscreen mode

And for iOS, ensure you have the latest Podfile settings:

use_react_native!(:new_architecture => true)
Enter fullscreen mode Exit fullscreen mode

Step 3: Migrate Your Native Modules

If you have custom native modules, you'll need to rewrite them as TurboModules. This involves updating your C++ bindings and ensuring they are JSI-compliant.

Step 4: Test and Optimize

With the new architecture enabled, test your app for potential breaking changes and performance improvements. Use tools like Flipper for debugging and performance analysis.

Benefits of the New Architecture

  • Improved Performance: Faster app startup and smoother UI.
  • Smaller Bundle Sizes: Reduced memory footprint with on-demand module loading.
  • Cross-Platform Consistency: More predictable performance across iOS and Android.
  • Easier Maintenance: Simplified module management and code sharing.

Real-World Project Example – Building a Native Module for Battery Health

To bring everything together, let’s walk through a small real-world project that leverages the new architecture. We’ll create a native module to get the battery health of the device.

Step 1: Creating the Native Module (Java)

Create a new file in your Android project:

// BatteryHealthModule.java
package com.yourapp;
import android.os.BatteryManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

public class BatteryHealthModule extends ReactContextBaseJavaModule {
    BatteryHealthModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "BatteryHealthModule";
    }

    @ReactMethod
    public void getBatteryHealth(Promise promise) {
        try {
            IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
            Intent batteryStatus = getReactApplicationContext().registerReceiver(null, ifilter);
            int health = batteryStatus.getIntExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
            String status;
            switch (health) {
                case BatteryManager.BATTERY_HEALTH_GOOD:
                    status = "Good";
                    break;
                case BatteryManager.BATTERY_HEALTH_OVERHEAT:
                    status = "Overheat";
                    break;
                case BatteryManager.BATTERY_HEALTH_DEAD:
                    status = "Dead";
                    break;
                case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
                    status = "Over Voltage";
                    break;
                default:
                    status = "Unknown";
            }
            promise.resolve(status);
        } catch (Exception e) {
            promise.reject("Error", e);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Using the Native Module (JavaScript)

import { NativeModules, Button, Text, View } from 'react-native';
import React, { useState } from 'react';

export default function App() {
    const [batteryHealth, setBatteryHealth] = useState(null);

    const fetchBatteryHealth = async () => {
        try {
            const health = await NativeModules.BatteryHealthModule.getBatteryHealth();
            setBatteryHealth(health);
        } catch (error) {
            console.error(error);
        }
    };

    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <Text>Battery Health: {batteryHealth !== null ? batteryHealth : 'Unknown'}</Text>
            <Button title="Get Battery Health" onPress={fetchBatteryHealth} />
        </View>
    );
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Testing the Module

Run your app and press the Get Battery Health button. You should see the current battery health displayed on the screen.

Benefits of This Example

  • Direct Native Access: Immediate access to battery health without bridging delays.
  • Efficient Memory Use: No unnecessary data copying, thanks to TurboModules.
  • Scalability: Easily extend this module to include more battery-related metrics.

Debugging React Native’s New Architecture

Debugging your app with the new architecture can feel a bit different, but with the right tools and approach, it’s straightforward and even fun!

1. Use Flipper – The Ultimate Debugging Tool

Flipper is the official React Native debugger that supports the new architecture:

  • Why Flipper?
    It shows logs, inspects network calls, views your component hierarchy, and profiles performance.

  • How to set up Flipper:

Flipper is integrated by default in React Native 0.72+. Just open Flipper on your machine and connect your device/emulator.

  • Inspect Fabric UI Components: The new Fabric architecture exposes detailed UI component trees you can inspect in Flipper’s React DevTools plugin.

2. Enable Debugging in React Native

  • For JavaScript debugging, use Hermes Debugger or Chrome Debugger:

    • Hermes debugger is faster and supports the new architecture better.
    • To enable, add in your metro.config.js:
  module.exports = {
    transformer: {
      experimentalImportSupport: false,
      inlineRequires: true,
    },
  };
Enter fullscreen mode Exit fullscreen mode
  • Use console.log as usual, but keep an eye on Flipper’s logs for native module outputs.

3. Debug TurboModules

  • Verify if TurboModules are loaded by inspecting your native modules in Flipper.
  • Use Android Studio or Xcode native debuggers to step through native code.
  • Add explicit logging in native modules to trace method calls.

4. Debugging JSI

  • Because JSI runs native code directly, debugging might require native debugger tools.
  • Use lldb (iOS) or gdb (Android) to debug C++ code.
  • Use console.log or logging macros in C++ to trace calls.

5. Performance Profiling

  • Use Flipper's React DevTools profiler to track rendering performance with Fabric.
  • Use native profilers (Android Profiler, Instruments on iOS) to find bottlenecks in native modules.

6. Common Debugging Tips

  • Always clear caches after switching architecture modes:
  npm start --reset-cache
Enter fullscreen mode Exit fullscreen mode
  • Rebuild your app after enabling the new architecture.
  • Monitor crash logs closely – sometimes native module errors bubble up asynchronously.

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

ACI image

ACI.dev: Fully Open-source AI Agent Tool-Use Infra (Composio Alternative)

100% open-source tool-use platform (backend, dev portal, integration library, SDK/MCP) that connects your AI agents to 600+ tools with multi-tenant auth, granular permissions, and access through direct function calling or a unified MCP server.

Check out our GitHub!