Skip to main content

Implementing New Architecture

1. Understanding Fabric Renderer

The Fabric Renderer is a new architecture in React Native that aims to improve performance, reliability, and extensibility of the rendering process. It replaces the legacy UI manager and provides a more efficient way to manage native views.

Feature Description Status
Fabric Enables faster rendering and better memory management NEW
Bridgeless Eliminates the need for a JavaScript-to-native bridge in some cases NEW
Turbo Modules Provides a more efficient way to access native modules NEW

Example: Enabling Fabric in React Native

// In android/app/src/main/java/.../MainActivity.java
// Add this to your MainActivity class:
// public class MainActivity extends ReactActivity {
//   @Override
//   protected String getMainComponentName() {
//     return "YourApp";
//   }
//
//   @Override
//   protected boolean isBridgeless() {
//     return true;
//   }
// }

// In ios/YourApp/AppDelegate.mm
// Add this to your AppDelegate:
&#x2F;&#x2F; #import <React/RCTBridge.h>
&#x2F;&#x2F; #import <React/RCTRootView.h>
&#x2F;&#x2F; #import <React/RCTFabricSurface.h>
Note: Fabric is still experimental and requires additional setup steps to enable. Ensure you are using React Native 0.68 or higher.

2. Understanding TurboModules

Property Description Notes
name Name of the TurboModule Must be unique within the app
version Version of the TurboModule Used for compatibility checks
methods List of methods exposed by the module Each method can have parameters and return types
events Events that can be emitted by the module Used for communication between native and JS layers

Example: Defining a TurboModule in JavaScript

import { TurboModuleRegistry } from 'react-native';

const TurboModule = TurboModuleRegistry.getEnforcing('MyTurboModule');

export default TurboModule;

Example: Defining a TurboModule in Native Code (iOS)

@objc(MyTurboModule)
class MyTurboModule: NSObject, TurboModule {
  @objc
  func getConstants() -> [String: Any]? {
    return [
      "version": "1.0"
    ]
  }

  @objc
  func fetchUser(id: String) -> String {
    return "User: \(id)"
  }
}

Example: Defining a TurboModule in Native Code (Android)

public class MyTurboModuleSpec extends NativeModuleSpec {
  @ReactMethod
  public void fetchUser(String id, Promise promise) {
    promise.resolve("User: " + id);
  }
}
Note: TurboModules are part of the New Architecture and require enabling it in your project configuration.
Warning: TurboModules are not supported in the Old Architecture. Ensure you have migrated to New Architecture before using them.

3. Enabling New Architecture

Platform Enablement Method Notes
iOS npx react-native config Run this command to generate the new config files
Android gradle.properties Add newArchEnabled=true to enable

Example: Enabling New Architecture on Android

newArchEnabled=true
Note: Ensure you have react-native@0.72+ to use New Architecture features.
Warning: Enabling New Architecture may require updating native dependencies to support the new architecture.

4. Migrating to New Architecture

Migration Steps

  1. Update React Native version to 0.71 or higher
  2. Enable the New Architecture in android/app/src/main/java/.../MainApplication.java
  3. Update build.gradle files to use newArchEnabled true
  4. Run npx react-native config to regenerate configs
  5. Update native modules to use TurboModules
  6. Test the app on both iOS and Android platforms
Step Description Files Modified
Version Update Upgrade to React Native 0.71+ package.json
New Arch Enable Set newArchEnabled true android/app/build.gradle
Config Regeneration Run npx react-native config react-native.config.js

Example: Enabling New Architecture in Android

public class MainApplication extends Application implements ReactApplication {
  private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          return "index";
        }

        // Enable New Architecture
        @Override
        protected boolean isNewArchEnabled() {
          return true;
        }
      };
}
Note: Ensure all native modules are updated to support TurboModules before enabling the new architecture.
Warning: Migrating to New Architecture requires updating native code and may break existing functionality if not done carefully.

5. Using JSI

JavaScript Interface (JSI) is a core part of React Native's New Architecture that enables direct communication between JavaScript and native modules without the overhead of a bridge. It allows for high-performance interactions by providing a way to call native code from JavaScript and vice versa.

Feature Description Usage
jsi::Runtime Represents the JavaScript runtime environment in which JSI operates. Used to access and manipulate JavaScript objects and functions from C++.
jsi::Function Represents a JavaScript function that can be called from C++. Allows calling JavaScript functions directly from native code.
jsi::Object Represents a JavaScript object accessible from C++. Used to interact with JavaScript objects and their properties.
jsi::Value Represents any JavaScript value (number, string, boolean, etc.). Used to pass values between JavaScript and native code.

Example: Calling a JavaScript Function from C++

#include <jsi/jsi.h>

using namespace facebook::jsi;

void callJSFunction(Runtime& runtime) {
  // Assume we have a JavaScript function named "myJSFunction"
  auto jsFunction = runtime global["myJSFunction"].asObject(runtime).asFunction(runtime);
  
  // Create arguments to pass to the function
  std::vector<Value> args;
  args.push_back(Value(42));
  args.push_back(Value("Hello"));
  
  // Call the JavaScript function
  jsFunction.call(runtime, args.data(), args.size());
}

Example: Creating a JavaScript Object from C++

#include <jsi/jsi.h>

using namespace facebook::jsi;

void createJSObject(Runtime& runtime) {
  // Create a new JavaScript object
  Object jsObject = Object(runtime);
  
  // Set properties on the object
  jsObject.setProperty(runtime, "name", String::createFromUtf8(runtime, "John"));
  jsObject.setProperty(runtime, "age", Value(30));
  
  // Access the object from JavaScript
  runtime global["myObject"] = jsObject;
}
Note: JSI is a powerful feature that allows for more efficient communication between JavaScript and native modules. It's particularly useful when performance is critical, such as in animations or real-time data processing.
Warning: When using JSI, ensure that you are managing memory correctly to avoid leaks or crashes. The jsi::Runtime and related objects must be handled with care to prevent undefined behavior.

6. Creating TurboModule Specs

Creating TurboModule specs in React Native involves defining the interface for native modules that can be called from JavaScript. These specs are written in a language-agnostic format and are used to generate the necessary code for both iOS and Android platforms.

Property Type Description Required
name string Name of the TurboModule Yes
functions object Functions exposed by the TurboModule No
constants object Constants exposed by the TurboModule No

Example: TurboModule Spec Definition

// TurboModuleSpec.js
import type { TurboModule } from &#x27;react-native&#x27;;

export interface Spec extends TurboModule {
  +getConstants: () => {+foo: string};
  +getValue: (value: string) => Promise<string>;
}

export default (requireNativeComponent(&#x27;MyTurboModule&#x27;) : Spec);

Example: Native Implementation (iOS)

// MyTurboModule.swift
import Foundation
import React

@objc(MyTurboModule)
class MyTurboModule: NSObject, RCTTurboModule {
  func getConstants() -> [String: Any] {
    return [&#x27;foo&#x27;: &#x27;bar&#x27;]
  }

  func getValue(_ value: String) -> RCTPromiseResolveBlock {
    return { (result) in
      // Implementation here
    }
  }

  static let moduleName = &#x27;MyTurboModule&#x27;
}

Example: Native Implementation (Android)

// MyTurboModule.java
package com.example;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Promise;

public class MyTurboModule extends ReactContextBaseJavaModule {
  public MyTurboModule(ReactApplicationContext reactContext) {
    super(reactContext);
  }

  @Override
  public String getName() {
    return &#x27;MyTurboModule&#x27;;
  }

  public Map<String, Object> getConstants() {
    return MapBuilder.of(&#x27;foo&#x27;, &#x27;bar&#x27;);
  }

  public void getValue(String value, Promise promise) {
    // Implementation here
  }
}

7. Implementing Codegen

Codegen in React Native is a process that generates native code from JavaScript or TypeScript files, particularly useful when working with the New Architecture. It helps in reducing boilerplate code and ensures type safety.

Feature Description Status
codegenConfig Configuration file to define code generation rules and targets. NEW
codegenSchema Defines the schema for generating native modules and components. NEW
codegenOutput Directory where generated files will be placed. NEW

Example: Configuring Codegen

{
  "codegenConfig": {
    "name": "MyModule",
    "type": "module",
    "schema": "./schema.json",
    "output": "./generated"
  }
}
Note: Codegen requires the New Architecture to be enabled in your project.
Warning: Generated files should not be manually edited as they will be overwritten during the next codegen run.
Command Description Usage
npx react-native codegen Generates native code based on the configuration. npx react-native codegen
npx react-native codegen --verbose Runs codegen with verbose logging. npx react-native codegen --verbose

8. Handling Concurrent Rendering

Feature Description Notes
Concurrent Mode Enables React to prepare multiple versions of the UI in advance Available in React 18+
useTransition Allows components to mark updates as low-priority Used for smoother UI transitions
useDeferredValue Defers the rendering of a value until after higher-priority updates Helps with performance in large lists

Example: useTransition

import React, { useState, useTransition } from 'react';

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

  const handleClick = () => {
    startTransition(() => {
      setCount(c => c + 1);
    });
  };

  return (
    <div>
      <button onClick={handleClick} disabled={isPending}>
        {isPending ? 'Loading...' : 'Increment'}
      </button>
      <p>Count: {count}</p>
    </div>
  );
}
Note: Concurrent rendering is an advanced feature that requires careful handling to avoid race conditions and ensure consistent state.

9. Using Bridgeless Mode

Bridgeless mode is a key feature of React Native's New Architecture that aims to improve performance and reduce memory usage by eliminating the need for a JavaScript-to-native bridge in many cases.

Feature Description Status
Bridgeless Mode Enables direct communication between JavaScript and native modules without the traditional bridge layer. NEW
Performance Improvement Reduces overhead and improves rendering performance by minimizing context switching. NEW
Memory Usage Decreases memory consumption by avoiding redundant data copying. NEW

Example: Enabling Bridgeless Mode

// In your app's main component or configuration
import { Platform } from 'react-native';

const isBridgeless = Platform.OS === 'android' && Platform.Version >= 30;

// For Android, enable bridgeless mode in MainActivity.java
// Add to your MainActivity.java:
// public class MainActivity extends ReactActivity {
//   @Override
//   protected String getMainComponentName() {
//     return "YourApp";
//   }
//
//   @Override
//   protected boolean isBridgelessEnabled() {
//     return BuildConfig.BRIDGELESS_ENABLED;
//   }
// }

// In build.gradle (app level), set:
// buildConfigField "boolean", "BRIDGELESS_ENABLED", "true"
Note: Bridgeless mode is currently in experimental phase for Android and requires API level 30 or higher.
Warning: Not all native modules are compatible with Bridgeless mode. Ensure that any custom native modules are updated to support this new architecture.

10. Understanding Performance Benefits

Benefit Description Details
Faster UI Updates Improved rendering performance Uses TurboModules and Fabric for faster UI updates. Reduces layout thrashing by batching operations.
Memory Efficiency Reduced memory overhead Optimized memory usage through better garbage collection and reduced native bridge overhead.
Native Module Performance Enhanced native module execution TurboModules provide a more efficient way to communicate between JavaScript and native modules, reducing latency.
Improved Startup Time Faster app launch times Optimized loading and initialization of components, reducing overall startup time.

Example: Performance Comparison

// Before New Architecture
const oldComponent = () => {
  return <View>
    <Text>Old rendering</Text>
  </View>;
};

// After New Architecture
const newComponent = () => {
  return <View>
    <Text>New rendering with Fabric</Text>
  </View>;
};
Note: Performance gains vary depending on the complexity of your app and how well it's optimized for the new architecture.

Was this article helpful?