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:
// #import <React/RCTBridge.h>
// #import <React/RCTRootView.h>
// #import <React/RCTFabricSurface.h>
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);
}
}
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 |
react-native@0.72+ to use New Architecture features.
4. Migrating to New Architecture
Migration Steps
- Update React Native version to 0.71 or higher
- Enable the New Architecture in
android/app/src/main/java/.../MainApplication.java - Update
build.gradlefiles to usenewArchEnabled true - Run
npx react-native configto regenerate configs - Update native modules to use TurboModules
- 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;
}
};
}
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;
}
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 'react-native';
export interface Spec extends TurboModule {
+getConstants: () => {+foo: string};
+getValue: (value: string) => Promise<string>;
}
export default (requireNativeComponent('MyTurboModule') : Spec);
Example: Native Implementation (iOS)
// MyTurboModule.swift
import Foundation
import React
@objc(MyTurboModule)
class MyTurboModule: NSObject, RCTTurboModule {
func getConstants() -> [String: Any] {
return ['foo': 'bar']
}
func getValue(_ value: String) -> RCTPromiseResolveBlock {
return { (result) in
// Implementation here
}
}
static let moduleName = 'MyTurboModule'
}
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 'MyTurboModule';
}
public Map<String, Object> getConstants() {
return MapBuilder.of('foo', 'bar');
}
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"
}
}
| 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>
);
}
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"
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>;
};