Prerequisites for Applications
It must meet a few prerequisites before enabling the new architecture in the application.
Use a React Native nightly release
You must now use a React Native nightly release to get the most recent updates. We will eventually suggest aiming for a minimal stable open-source release.
This article is prepared, assuming you’re running a specific nightly release. The intended nightly release may be updated when new revisions of this guide are available. The nightly version that we’ll utilize for the rest of this guide is 0.0.0-20220201-2008-79975d146.
We recommend updating your app to the most recent open-source version before upgrading to a specific nightly release. By initially updating to a stated open-source release, you would be able to use tools such as the upgrade helper to evaluate what extra modifications may be needed for your project.
The most recent stable release is 0.68.2 as of this writing. After successfully upgrading your project to this version, you may target the 0.0.0-20220201-2008-79975d146 nightly release. You can target this nightly release in the same way you would any other React Native version:
Install react-native-codegen
Check that you are using the most recent version of the react-native-codegen NPM package. It is 0.0.13 at the time of this writing.
Android specifics
There are various prerequisites for using the new architecture on Android:
- Gradle 7.x and the Android Gradle Plugin 7.x is being used.
- Making use of the new React Gradle Plugin
- Creating React-native from the Source
You may upgrade Gradle by running:
While it must change the AGP version at the com.android.tools. Build:Gradle dependency line in the top-level build.gradle file.
If you’re ready, let’s install the new Gradle plugin, available as an NPM package named react-native-Gradle-plugin. You may accomplish so by using:
You may check if the package is already installed by doing the following:
You may now change your top-level settings.Gradle file to add the following line at the bottom of the file:
include(“:ReactAndroid”)
project(“:ReactAndroid”).projectDir = file(‘../node_modules/react-native/ReactAndroid’)
Then, in the top-level Gradle file, add the following lines:
// …dependencies {// Make sure that AGP is at least at version 7.xclasspath(“com.android.tools.build:gradle:7.0.4”)// Add those linesclasspath(“com.facebook.react:react-native-gradle-plugin”)classpath(“de.undercouch:gradle-download-task:4.1.2”)}}
Include the following in your module-level Gradle file (typically app/build.gradle[.kts]):
apply plugin: “com.android.application”
apply plugin: “com.facebook.react”// Add those lines as wellreact {
reactRoot = rootProject.file(“../node_modules/react-native/”)
codegenDir = rootProject.file(“../node_modules/react-native-codegen/”)}
Finally, change your project to utilize the react-native requirement from the source instead of a pre-compiled object from the NPM package. It is required since the subsequent setup will depend on creating native code using the source.
Let’s update the following line in your module-level build.Gradle (the one in the app/ folder):
// Replace this:implementation “com.facebook.react:react-native:+” // From node_modules// With this:implementation project(“:ReactAndroid”) // From node_modules
Use Hermes
Hermes is an open-source JavaScript engine designed specifically for React Native. We strongly advise you to use Hermes in the application. With Hermes configured, you would be able to debug your JavaScript code directly in Flipper using the JavaScript debugger.
iOS: Enable C++17 language feature support
To enable C++17 language features, you must change your Xcode project settings.
Instructions
- In the Project Navigator on the left, select your project (e.g., MyXcodeApp)
- Then, in the middle pane, check sure your project is chosen.
- Look for C++ Language Dialect or CLANG CXX LANGUAGE STANDARD in Build Settings.
- Select C++17 from the dropdown menu (or type “c++17” straight into the value field).
If you did everything successfully, your diff should display the following modifications to your project file: CLANG_CXX_LANGUAGE_STANDARD = “c++17”
Folly should also be enabled in your project, and it should occur instantly after the library requirement is detected. Thus no more modifications to your project are required.
iOS: Use Objective-C++ (.mm extension)
Objective-C or C++ can be used to create TurboModules. Any source files containing C++ code should be using the.mm file extension to handle both instances. This extension is equivalent to Objective-C++, a language variation that supports the usage of C++ and Objective-C in source files.
To guarantee that file references remain in your project, use Xcode to rename existing files. Before rebuilding the program, you may need to clear the build folder (Project Clean Build Folder). If the file is changed outside of Xcode, you may have to locate the new file by clicking on the old.m file references.
iOS: TurboModules: Make sure your App Offers an RCTCxxBridgeDelegate
To configure the TurboModule system, add more code to your AppDelegate that interacts with the bridge. Before you begin, rename your AppDelegate file for using the.mm extension.
Your AppDelegate will now comply with RCTCxxBridgeDelegate. Begin by including the following imports at the beginning of your AppDelegate file:
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTJSIExecutorRuntimeInstaller.h>
Then, as an RCTCxxBridgeDelegate provider, declare your app delegate:
@interface AppDelegate () <RCTCxxBridgeDelegate> { // … } @end
You must implement the jsExecutorFactoryForBridge: function to comply with the RCTCxxBridgeDelegate protocol. Typically, you should get a JSCExecutorFactory or HermesExecutorFactory here, and we will use it later to install our TurboModules bindings.
You may use the jsExecutorFactoryForBridge: method as follows:
– (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge{return std::make_unique<facebook::react::HermesExecutorFactory>(facebook::react::RCTJSIExecutorRuntimeInstaller([bridge](facebook::jsi::Runtime &runtime) {if (!bridge) {return;}}));}
Enabling the New NativeModule System (TurboModule) in your App
Android
1. Enable NDK and the native build
The code-gen will generate some Java and C++ code, which we must now build.
Let’s go through your module-level build.gradle to add the following two externalNativeBuild blocks within the android block:
externalNativeBuild {ndkBuild {arguments “APP_PLATFORM=android-21″,”APP_STL=c++_shared”,”NDK_TOOLCHAIN_VERSION=clang”,”GENERATED_SRC_DIR=$buildDir/generated/source”,”PROJECT_BUILD_DIR=$buildDir”,”REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid”,”REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build”cFlags “-Wall”, “-Werror”, “-fexceptions”, “-frtti”, “-DWITH_INSPECTOR=1″cppFlags “-std=c++17″targets “myapplication_appmodules” }}}
externalNativeBuild { ndkBuild { path “$projectDir/src/main/jni/Android.mk” } } }
Let’s add the following part to the same build.gradle file, under the same android:
// …def reactAndroidProjectDir = project(‘:ReactAndroid’).projectDirdef packageReactNdkLibs = tasks.register(“packageReactNdkLibs”, Copy) {dependsOn(“:ReactAndroid:packageReactNdkLibsForBuck”)dependsOn(“generateCodegenArtifactsFromSchema”)from(“$reactAndroidProjectDir/src/main/jni/prebuilt/lib”)into(“$buildDir/react-ndk/exported”)}afterEvaluate {preBuild.dependsOn(packageReactNdkLibs)configureNdkBuildDebug.dependsOn(preBuild)configureNdkBuildRelease.dependsOn(preBuild) }packagingOptions {pickFirst ‘**/libhermes.so’pickFirst ‘**/libjsc.so’ } }
Finally, under the src/main/jni folder, create a Makefile named Android.mk with the following points:
include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
include $(GENERATED_SRC_DIR)/codegen/jni/Android.mkinclude $(CLEAR_VARS)LOCAL_PATH := $(THIS_DIR)
LOCAL_MODULE := myapplication_appmodulesLOCAL_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni# Please note as one of the library listed is libreact_codegen_samplelibrary
# This name will be generated as libreact_codegen_<library-name>
# where <library-name> is the one you specified in the Gradle configuration
LOCAL_SHARED_LIBRARIES := libjsi \
libfbjni \
libglog \
libfolly_json \
libyoga \
libreact_nativemodule_core \
libturbomodulejsijni \
librrc_view \
libreact_render_core \
libreact_render_graphics \
libfabricjni \
libfolly_futures \
libreact_debug \
libreact_render_componentregistry \
libreact_render_debug \
libruntimeexecutor \
libreact_codegen_rncore \
libreact_codegen_samplelibraryLOCAL_CFLAGS := \
-DLOG_TAG=\”ReactNative\”
LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wallinclude $(BUILD_SHARED_LIBRARY)
This configuration will execute a native build on the project and compile the C++ files the codegen has generated. The native build will be running alongside the Gradle task: app:externalNativeBuildDebug
You can now test that everything is working properly by starting your Android app: yarn react-native run-android
2. Java – Provide a ReactPackageTurboModuleManagerDelegate
It’s finally time to put the TurboModule to work. First, we’ll need to make a ReactPackageTurboModuleManagerDelegate subclass similar to this:
import com.facebook.jni.HybridData;import com.facebook.react.ReactPackage;import com.facebook.react.ReactPackageTurboModuleManagerDelegate;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.soloader.SoLoader;import java.util.List;public class MyApplicationTurboModuleManagerDelegate extends ReactPackageTurboModuleManagerDelegate {private static volatile boolean sIsSoLibraryLoaded;protected MyApplicationTurboModuleManagerDelegate(ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {super(reactApplicationContext, packages); }protected native HybridData initHybrid();public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {protected MyApplicationTurboModuleManagerDelegate build(ReactApplicationContext context, List<ReactPackage> packages) {return new MyApplicationTurboModuleManagerDelegate(context, packages); } }@Override
protected synchronized void maybeLoadOtherSoLibraries() {// Prevents issues with initializer interruptions.
if (!sIsSoLibraryLoaded) { SoLoader.loadLibrary(“myapplication_appmodules”); sIsSoLibraryLoaded = true; } } }
Please keep in mind that the SoLoader.loadLibrary argument (in this example, “myapplication appmodules”) should match the one set for LOCAL MODULE:= inside the Android.mk file you previously built.
This class will subsequently be in charge of loading the TurboModules and the native library built with the NDK during runtime.
3. Adapt your ReactNativeHost to use the ReactPackageTurboModuleManagerDelegate
The class you developed may then be sent to your ReactNativeHost. You may find your ReactNativeHost by looking for the getReactNativeHost function (). The ReactNativeHost is often found within your Application class.
Once you’ve found it, add the getReactPackageTurboModuleManagerDelegateBuilder function as seen in the code below:
private final ReactNativeHost mReactNativeHost =new ReactNativeHost(this) {@Overridepublic boolean getUseDeveloperSupport() { /* … */ }@Overrideprotected List<ReactPackage> getPackages() { /* … */ }@Overrideprotected String getJSMainModuleName() {/* … */ }@NonNull@Overrideprotected ReactPackageTurboModuleManagerDelegate.Builder getReactPackageTurboModuleManagerDelegateBuilder() {return new MyApplicationTurboModuleManagerDelegate.Builder(); } }; }
4. Extend the getPackages() from your ReactNativeHost to use the TurboModule
On the ReactNativeHost, we need to add the newly built TurboModule to the getPackages() function. Make the following changes to the method:
private final ReactNativeHost mReactNativeHost =new ReactNativeHost(this) {@Overridepublic boolean getUseDeveloperSupport() { /* … */ }@Overrideprotected List<ReactPackage> getPackages() {List<ReactPackage> packages = new PackageList(this).getPackages();// Add those linespackages.add(new TurboReactPackage() {@Nullable@Overridepublic NativeModule getModule(String name, ReactApplicationContext reactContext) {if (name.equals(NativeAwesomeManager.NAME)) {return new NativeAwesomeManager(reactContext);} else {return null; } }@Overridepublic ReactModuleInfoProvider getReactModuleInfoProvider() { return () -> {final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();moduleInfos.put( NativeAwesomeManager.NAME,new ReactModuleInfo( NativeAwesomeManager.NAME,”NativeAwesomeManager”, false, // canOverrideExistingModulefalse, // needsEagerInittrue, // hasConstantsfalse, // isCxxModuletrue // isTurboModule ) );return moduleInfos; }; } }); return packages; }@Overrideprotected String getJSMainModuleName() {/* … */ }@NonNull@Overrideprotected ReactPackageTurboModuleManagerDelegate.Builder getReactPackageTurboModuleManagerDelegateBuilder() { return new MyApplicationTurboModuleManagerDelegate.Builder(); } };
5. C++ Provide a native implementation for the methods in your *TurboModuleDelegate class
If you look closely at the previously created class MyApplicationTurboModuleManagerDelegate, you will find that some of the functions are native.
As a result, you’ll need to include certain C++ classes to implement those functions. You will require the following files, which must place in the src/main/jni folder:
- h The TurboModule Delegate’s header file.
- cpp The use of the header above file.
- h A TurboModule provider header file in which you may define which TurboModules to run.
- cpp The implementation of the previously specified header file.
- CPP The location of the initialization code. TurboModule, in particular, makes use of FBJNI. Therefore the initialization for such libraries is stored there.
The following information should be included in such files:
MyApplicationTurboModuleManagerDelegate.h
Please keep in mind that the kJavaDescriptor should be modified to match the package name you choose for your project.
#include <memory>
#include <string>
#include <ReactCommon/TurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
namespace facebook { namespace react { class MyApplicationTurboModuleManagerDelegate : public jni::HybridClass<MyApplicationTurboModuleManagerDelegate, TurboModuleManagerDelegate> { public:
// Adapt it to the package you used for your Java class.
static constexpr auto kJavaDescriptor = “Lcom/awesomeproject/MyApplicationTurboModuleManagerDelegate;”;
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
static void registerNatives();
std::shared_ptr<TurboModule> getTurboModule(const std::string name, const std::shared_ptr<CallInvoker> jsInvoker) override;
std::shared_ptr<TurboModule> getTurboModule(const std::string name, const JavaTurboModule::InitParams ¶ms) override;
private:
friend HybridBase;
using HybridBase::HybridBase; }; } // namespace react } // namespace facebook
MyApplicationTurboModuleManagerDelegate.cpp
#include “MyApplicationModuleProvider.h”namespace facebook {namespace react {jni::local_ref<MyApplicationTurboModuleManagerDelegate::jhybriddata> MyApplicationTurboModuleManagerDelegate::initHybrid(jni::alias_ref<jhybridobject>) {return makeCxxInstance();}void MyApplicationTurboModuleManagerDelegate::registerNatives() {registerHybrid({makeNativeMethod(“initHybrid”, MyApplicationTurboModuleManagerDelegate::initHybrid), }); }std::shared_ptr<TurboModule> MyApplicationTurboModuleManagerDelegate::getTurboModule(const std::string name, const std::shared_ptr<CallInvoker> jsInvoker) {// Not implemented yet: provide pure-C++ NativeModules here.return nullptr; }std::shared_ptr<TurboModule> MyApplicationTurboModuleManagerDelegate::getTurboModule(const std::string name, const JavaTurboModule::InitParams ¶ms) {return MyApplicationModuleProvider(name, params); } } // namespace react } // namespace facebook
MyApplicationModuleProvider.h
#include <memory>#include <string>#include <ReactCommon/JavaTurboModule.h>namespace facebook {namespace react {std::shared_ptr<TurboModule> MyApplicationModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms);} // namespace react} // namespace facebook
MyApplicationModuleProvider.cpp
Please modify the samplelibrary.h import to use the same library name that you used while building the applications. It is the C++-created file produced by codegen.
If you have more than one TurboModule, you can select more than one supplier here. In this case, we specifically seek a TurboModule from the sample library (the one we provided) and drop it back to the rncore Module Provider.
#include <rncore.h>#include <samplelibrary.h>namespace facebook {namespace react {std::shared_ptr<TurboModule> MyApplicationModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) {auto module = samplelibrary_ModuleProvider(moduleName, params);if (module != nullptr) {return module; }return rncore_ModuleProvider(moduleName, params); }} // namespace react} // namespace facebook
OnLoad.cpp
#include “MyApplicationTurboModuleManagerDelegate.h”JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {return facebook::jni::initialize(vm, [] {facebook::react::MyApplicationTurboModuleManagerDelegate::registerNatives();});}
6. Enable the useTurboModules flag in your Application onCreate
Finally, you may enable TurboModule functionality in the application. To do so, enable the useTurboModule flag in the Application onCreate function.
@Overridepublic void onCreate() {ReactFeatureFlags.useTurboModules = true;//…}
It’s now time to re-run your Android app to ensure that everything is working correctly: yarn react-native run-android
iOS
1. Provide a TurboModuleManager Delegate
Include the following imports at the start of the bridge delegate (for example, AppDelegate.mm):
#import <React/CoreModulesPlugins.h>
You must also specify that the AppDelegate follows the RCTTurboModuleManagerDelegate protocol and add an instance variable for the Turbo Module manager:
// … RCTTurboModuleManager *_turboModuleManager; } @end
To comply with the RCTTurboModuleManagerDelegate standard, you must implement the following three methods:
- getModuleClassFromName: – The Class for a native module should be returned by this function. To handle the default core modules, use the RCTCoreModulesClassProvider() function.
- getTurboModule:jsInvoker: – This should return nullptr. This approach might be used in the future to support C++ TurboModules.
- getModuleInstanceFromClass:moduleClass: – When your TurboModules are initialized, you may use this method to conduct any side effects. It is the TurboModule equivalent of the extraModulesForBridge function on your bridge delegate. You must now initialize the default RCTNetworking and RCTImageLoader modules as shown below.
TurboModuleManagerDelegate Example
Take notice of getModuleInstanceFromClass: in the following cases since it contains the required instantiation of numerous essential modules that you’ll need to include within your application. It may not be necessary for the long run.
// …#import <React/RCTDataRequestHandler.h>#import <React/RCTHTTPRequestHandler.h>#import <React/RCTFileRequestHandler.h>#import <React/RCTNetworking.h>#import <React/RCTImageLoader.h>#import <React/RCTGIFImageDecoder.h>#import <React/RCTLocalAssetImageLoader.h>#import <React/CoreModulesPlugins.h>#import <ReactCommon/RCTTurboModuleManager.h>// …#pragma mark RCTTurboModuleManagerDelegate- (Class)getModuleClassFromName:(const char *)name {return RCTCoreModulesClassProvider(name); }- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)namejsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker { return nullptr; }- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass {// Set up the default RCTImageLoader and RCTNetworking modules.if (moduleClass == RCTImageLoader.class) { return [[moduleClass alloc] initWithRedirectDelegate:nilloadersProvider:^NSArray<id<RCTImageURLLoader>> *(RCTModuleRegistry * moduleRegistry) {return @ [[RCTLocalAssetImageLoader new]]; } decodersProvider:^NSArray<id<RCTImageDataDecoder>> *(RCTModuleRegistry * moduleRegistry) { return @ [[RCTGIFImageDecoder new]]; }];} else if (moduleClass == RCTNetworking.class) { return [[moduleClass alloc]initWithHandlersProvider:^NSArray<id<RCTURLRequestHandler>> *( RCTModuleRegistry *moduleRegistry) {return @[ [RCTHTTPRequestHandler new], [RCTDataRequestHandler new], [RCTFileRequestHandler new], ]; }]; }// No custom initializer here.return [moduleClass new]; }
2. Install TurboModuleManager JavaScript Bindings
Next, in the bridge delegate’s jsExecutorFactoryForBridge: function, build an RCTTurboModuleManager and install the JavaScript bindings:
– (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge{// Add these lines to create a TurboModuleManagerif (RCTTurboModuleEnabled()) {_turboModuleManager =[[RCTTurboModuleManager alloc] initWithBridge:bridgedelegate:selfjsInvoker:bridge.jsCallInvoker];// Necessary to allow NativeModules to lookup TurboModules[bridge setRCTTurboModuleRegistry:_turboModuleManager];if (!RCTTurboModuleEagerInitEnabled()) {/*** Instantiating DevMenu has the side effect of registering* shortcuts for CMD + d, CMD + i, and CMD + n via RCTDevMenu.* Therefore, when TurboModules are enabled, we must manually create this* NativeModule. */[_turboModuleManager moduleForName:”DevMenu”]; } }// Add this line…__weak __typeof(self) weakSelf = self;// If you want to use the `JSCExecutorFactory,` remember to add the `#import<React/JSCExecutorFactory.h>`// import statement on top.return std::make_unique<facebook::react::HermesExecutorFactory>(facebook::react::RCTJSIExecutorRuntimeInstaller([weakSelf, bridge](facebook::jsi::Runtime &runtime) {if (!bridge) { return; }// And add these lines to install the bindings…__typeof(self) strongSelf = weakSelf;if (strongSelf) { facebook::react::RuntimeExecutor syncRuntimeExecutor = [&](std::function<void(facebook::jsi::Runtime & runtime_)> &&callback) { callback(runtime); };[strongSelf->_turboModuleManager installJSBindingWithRuntimeExecutor:syncRuntimeExecutor]; } })); }
3. Enable TurboModule System
Finally, activate TurboModules in the app by running the following sentence in your app delegate before React Native is started (e.g., within didFinishLaunchingWithOptions:):
RCTEnableTurboModule(YES);
Example
@implementation AppDelegate
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ RCTEnableTurboModule(YES);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self
launchOptions:launchOptions];
// …
return YES; }
Enabling the New Renderer (Fabric) in your App
Android
1. Provide a JSIModulePackage inside your ReactNativeHost
To enable Fabric in your project, you must include a JSIModulePackage within your ReactNativeHost. If you followed the TurboModule part of this guide, you may find your ReactNativeHost by looking for the getReactNativeHost function (). The ReactNativeHost is often found within your Application class.
Once you’ve found it, add the getJSIModulePackage function as seen in the code below:
public class MyApplication extends Application implements ReactApplication {private final ReactNativeHost mReactNativeHost =new ReactNativeHost(this) {// Add those lines:@Nullable@Overrideprotected JSIModulePackage getJSIModulePackage() {return new JSIModulePackage() {@Overridepublic List<JSIModuleSpec> getJSIModules(final ReactApplicationContext reactApplicationContext,final JavaScriptContextHolder jsContext) {final List<JSIModuleSpec> specs = new ArrayList<>();specs.add(new JSIModuleSpec() {@Overridepublic JSIModuleType getJSIModuleType() {return JSIModuleType.UIManager; }@Overridepublic JSIModuleProvider<UIManager> getJSIModuleProvider() { final ComponentFactory componentFactory = new ComponentFactory();CoreComponentsRegistry.register(componentFactory);final ReactInstanceManager reactInstanceManager = getReactInstanceManager();ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));return new FabricJSIModuleProvider( reactApplicationContext, componentFactory, new EmptyReactNativeConfig(), viewManagerRegistry); } }); return specs; } }; } }; }
2. Make sure your call setIsFabric on your Activity’s ReactRootView
Make sure you use setIsFabric on the ReactRootView within your Activity class. You may need to build a ReactActivityDelegate.
// Add the Activity Delegate if you don’t have one already.public static class MainActivityDelegate extends ReactActivityDelegate {public MainActivityDelegate(ReactActivity activity, String mainComponentName) {super(activity, mainComponentName);}@Overrideprotected ReactRootView createRootView() {ReactRootView reactRootView = new ReactRootView(getContext());// Make sure to call setIsFabric(true) on your ReactRootViewreactRootView.setIsFabric(true);return reactRootView; } }// Make sure to override the `createReactActivityDelegate()` method.@Overrideprotected ReactActivityDelegate createReactActivityDelegate() { return new MainActivityDelegate(this, getMainComponentName()); } }
The reactRootView.setIsFabric(true) call is critical in this code because it enables the new renderer for such an Activity.
You can now test that everything is working correctly by starting your Android app: yarn react-native run-android
To prove that Fabric is functioning correctly, you should now see the following log in your Metro terminal log:
BUNDLE ./App.js
LOG Running “App” with {“fabric”:true,”initialProps”:{},”rootTag”:1}
Migrating Android ViewManagers
To begin, ensure that you follow the Enabling the New Renderer (Fabric) guidelines in the Android Application. Furthermore, we will presume that you followed the rules in Enabling the New NativeModule System (TurboModule) in the Android Application because the Makefile (Android.mk) or other native build setup stages are covered there and will not be covered here.
JavaScript changes
- Follow these steps to ensure that your other JS updates are ready. Get your JavaScript code for the new React Native Renderer (Fabric).
- Change the requireNativeComponent call to codegenNativeComponent. It instructs the JS codegen to begin producing the component’s native implementation, consisting of C++ and Java classes. It’s how the WebView component appears:
import codegenNativeComponent from
‘react-native/Libraries/Utilities/codegenNativeComponent’;
// babel-plugin-codegen will replace the function call to use NativeComponentRegistry
// ‘RCTWebView’ is interopped by RCTFabricComponentsPlugins
export default (codegenNativeComponent<NativeProps>(
‘RCTWebView’,
): HostComponent<NativeProps>);
- [Flow users] Ensure your native component’s properties have Flow types since the JS codegen utilizes these types to produce the type-safe native execution of the component. The codegen creates C++ classes during the build process, ensuring that the native execution is always up to date with its JS interface. Make use of these C++-compatible kinds.
RNTMyNativeViewNativeComponent.js
import type {Int32} from ‘react-native/Libraries/Types/CodegenTypes’;
import codegenNativeComponent from ‘react-native/Libraries/Utilities/codegenNativeComponent’;
import type {HostComponent} from ‘react-native’;
import type {ViewProps} from ‘react-native/Libraries/Components/View/ViewPropTypes’;
type NativeProps = $ReadOnly<{|
…ViewProps, // This is required.
someNumber: Int32,
|}>;
[…]
export default (codegenNativeComponent<NativeProps>(
‘RNTMyNativeView’, ): HostComponent<NativeProps>);
- [TypeScript users] We are presently considering TypeScript support.
Native/Java Changes
1. Update (or create) the ViewManager to use the Codegen-generated classes.
You must specifically design the created ViewManagerInterface and provide events to the generated ViewManagerDelegate. Your ViewManager might follow this structure. In this case, the MyNativeView class is an Android View equivalent (like a subclass of LinearLayout, Button, TextView, etc.)
// View manager for MyNativeView components.@ReactModule(name = MyNativeViewManager.REACT_CLASS)public class MyNativeViewManager extends SimpleViewManager<MyNativeView>implements RNTMyNativeViewManagerInterface<MyNativeView> {public static final String REACT_CLASS = “RNTMyNativeView”;private final ViewManagerDelegate<MyNativeView> mDelegate;public MyNativeViewManager() {mDelegate = new RNTMyNativeViewManagerDelegate<>(this); }@Nullable@Overrideprotected ViewManagerDelegate<MyNativeView> getDelegate() {return mDelegate; }@NonNull@Overridepublic String getName() {return REACT_CLASS; }@NonNull@Overrideprotected MyNativeView createViewInstance(@NonNull ThemedReactContext reactContext) {return new MyNativeView(reactContext); } }
2. Add your ViewManager to one of your application’s Packages.
Modify the getPackages function in the ReactNativeHost to add the following:
public class MyApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() { /* … */ }
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// … other packages or `TurboReactPackage added` here…
// Add those lines.
packages.add(new ReactPackage() {
@NonNull
@Override
public List<NativeModule> createNativeModules(
@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList(); }
@NonNull
@Override
public List<ViewManager> createViewManagers(
@NonNull ReactApplicationContext reactContext) {
// Your ViewManager is returned here.
return Collections.singletonList(new MyNativeViewManager()); } });
return packages; } }; }
3. Add a Fabric Component Registry
You must build a new component Registry to register your components for Fabric discovery. Let’s fill the MyComponentsRegistry file with the following information.
As you’ll see, some methods are native(), which we will write in C++ in the following section.
package com.awesomeproject;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.soloader.SoLoader;
@DoNotStrip
public class MyComponentsRegistry {
static { SoLoader.loadLibrary(“fabricjni”); }
@DoNotStrip private final HybridData mHybridData;
@DoNotStrip
private native HybridData initHybrid(ComponentFactory componentFactory);
@DoNotStrip
private MyComponentsRegistry(ComponentFactory componentFactory) {
mHybridData = initHybrid(componentFactory); }
@DoNotStrip
public static MyComponentsRegistry register(ComponentFactory componentFactory) {
return new MyComponentsRegistry(componentFactory); } }
4. Register your custom Fabric Component Registry
Finally, let’s modify the getJSIModulePackage function from the ReactNativeHost to enroll your Component Registry with the Core one:
public class MyApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Nullable
@Override
protected JSIModulePackage getJSIModulePackage() {
return new JSIModulePackage() {
@Override
public List<JSIModuleSpec> getJSIModules(
final ReactApplicationContext reactApplicationContext,
final JavaScriptContextHolder jsContext) {
final List<JSIModuleSpec> specs = new ArrayList<>();
specs.add(new JSIModuleSpec() {
// …
@Override
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
final ComponentFactory componentFactory = new ComponentFactory();
CoreComponentsRegistry.register(componentFactory);
// Add this line just below CoreComponentsRegistry.register
MyComponentsRegistry.register(componentFactory);
// … } });
return specs; } }; } }; }
Native/C++ Changes
It is now time to write a C++ code for your MyComponentsRegistry:
1. Create a header file: MyComponentsRegistry.h
The file must be placed in the src/main/jni directory. Please keep in mind that the kJavaDescriptor must be modified to match the package name you choose for your project.
MyComponentsRegistry.h
#pragma once
#include <ComponentFactory.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
class MyComponentsRegistry
: public facebook::jni::HybridClass<MyComponentsRegistry> {
public:
constexpr static auto kJavaDescriptor =
“Lcom/awesomeproject/MyComponentsRegistry;”;
static void registerNatives();
MyComponentsRegistry(ComponentFactory *delegate);
private:
friend HybridBase;
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
sharedProviderRegistry();
const ComponentFactory *delegate_;
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate); };
} // namespace react
} // namespace facebook
2. Create an implementation file: MyComponentsRegistry.cpp
The file should be beside ‘MyComponentsRegistry.h’ in the src/main/jni folder.
#include “MyComponentsRegistry.h”#include <CoreComponentsRegistry.h>#include <fbjni/fbjni.h>#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>#include <react/renderer/components/rncore/ComponentDescriptors.h>#include <react/renderer/components/samplelibrary/ComponentDescriptors.h>namespace facebook {namespace react {MyComponentsRegistry::MyComponentsRegistry(ComponentFactory *delegate): delegate_(delegate) {}std::shared_ptr<ComponentDescriptorProviderRegistry const>MyComponentsRegistry::sharedProviderRegistry() {auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();providerRegistry->add(concreteComponentDescriptorProvider<RNTMyNativeViewComponentDescriptor>());return providerRegistry; }jni::local_ref<MyComponentsRegistry::jhybriddata>MyComponentsRegistry::initHybrid(jni::alias_ref<jclass>,ComponentFactory *delegate) {auto instance = makeCxxInstance(delegate);auto buildRegistryFunction =[](EventDispatcher::Weak const &eventDispatcher,ContextContainer::Shared const &contextContainer)-> ComponentDescriptorRegistry::Shared { auto registry = MyComponentsRegistry::sharedProviderRegistry()->createComponentDescriptorRegistry( {eventDispatcher, contextContainer});auto mutableRegistry = std::const_pointer_cast<ComponentDescriptorRegistry>(registry);mutableRegistry->setFallbackComponentDescriptor(std::make_shared<UnimplementedNativeViewComponentDescriptor>(ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr}));return registry; };
delegate->buildRegistryFunction = buildRegistryFunction; return instance; }
void MyComponentsRegistry::registerNatives() { registerHybrid({
makeNativeMethod(“initHybrid”, MyComponentsRegistry::initHybrid), }); } } // namespace react } // namespace facebook
3. Load your file in the OnLoad.cpp
You must have an OnLoad.cpp file in the src/main/jni folder if you followed the TurboModule guidelines. Add the following code to load the MyComponentsRegistry class:
OnLoad.cpp
#include “MyApplicationTurboModuleManagerDelegate.h”// Add this import#include “MyComponentsRegistry.h”JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {return facebook::jni::initialize(vm, [] {facebook::react::MyApplicationTurboModuleManagerDelegate::registerNatives();// Add this linefacebook::react::MyComponentsRegistry::registerNatives(); }); }
You can now test that everything is working correctly by starting your Android app: yarn react-native run-android
iOS
1. Enable Fabric in Podfile
Changes should be made to your Podfile. Some examples may be found in RNTester & rn-demo-app.
Podfile
# Add the following line at the top of Podfile.
# Codegen produces files/classes that share names, and it will show the warning.
# deterministic_uuids option surpresses the warning.
target ‘Some App’ dopods()enddef pods()# Get configconfig = use_native_modules!# Use env variables to turn it on/off.fabric_enabled = ENV[‘USE_FABRIC’]use_react_native!(…# Modify here if your app root path isn’t the same as this one.:app_path => “#{Dir.pwd}/..”,# Pass the flag to enable fabric to use_react_native!.:fabric_enabled => fabric_enabled ) End
2. Update your root view
Your configuration determines the method you use to render your app using Fabric. Here’s an example of activating Fabric in your project using the RN FABRIC ENABLED compiler flag. As an example, consider Tester’s RN-AppDelegate.
AppDelegate.mm
#ifdef RN_FABRIC_ENABLED
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <react/config/ReactNativeConfig.h>
#endif
@interface AppDelegate () <RCTCxxBridgeDelegate,
RCTTurboModuleManagerDelegate> {
#ifdef RN_FABRIC_ENABLED
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer; #endif
// Find a line that defines rootView and replace/edit with the following lines.
#ifdef RN_FABRIC_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert(“ReactNativeConfig”, _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc]
initWithBridge:bridge
contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
UIView *rootView = [[RCTFabricSurfaceHostingProxyRootView alloc] initWithBridge:bridge
moduleName:<#moduleName#>
initialProperties:@{}];
#else
// Current implementation to define rootview.
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:<#moduleName#>
initialProperties:@{}]; #endif
3. Add Babel Plugins
It will start the codegen, which will run during the metro building process.
module.exports = {presets: [‘module:metro-react-native-babel-preset’],plugins: [‘@babel/plugin-proposal-class-properties’,’./node_modules/react-native/packages/babel-plugin-codegen’]};
4. Run pod install
// Run pod install with the flags
Conclusion
Most JavaScript application code does not need to change due to the new architecture in terms of backward compatibility. However, the Java/ObjC code for custom View Managers and Native Modules must be changed, although many may be code-modified to work with the new system. A compatibility layer that allows bespoke View Managers and Native Modules to continue operating in the new system may also be built.
In terms of timeframes, most of the JSI code has already been deposited in the repository. A considerable percentage of the Fabric code is already in the repository, and TurboModules upgrades are still being released. It is generally backward compatible; there is no need for a single release date but rather a steady rollout.
IT consultants specializing in custom software development and mobile app development services can provide valuable assistance in this transition. If you need any help with implementing it, contact us for migration support.