Fabric Native Components
This documentation is still experimental and details are subject to changes as we iterate. Feel free to share your feedback on the discussion inside the working group for this page.
Moreover, it contains several manual steps. Please note that this won't be representative of the final developer experience once the New Architecture is stable. We're working on tools, templates and libraries to help you get started fast on the New Architecture, without having to go through the whole setup.
A Fabric Native Component is a Native Component rendered on the screen using the Fabric Renderer. Using Fabric Native Components instead of Legacy Native Components allows us to reap all the benefits of the New Architecture:
- Strongly typed interfaces that are consistent across platforms.
- The ability to write your code in C++, either exclusively or integrated with another native platform language, hence reducing the need to duplicate implementations across platforms.
- The use of JSI, a JavaScript interface for native code, which allows for more efficient communication between native and JavaScript code than the bridge.
A Fabric Native Component is created starting from a JavaScript specification. Then Codegen creates some C++ scaffolding code to connect the component-specific logic (for example, accessing some native platform capability) to the rest of the React Native infrastructure. The C++ code is the same for all the platforms. Once the component is properly connected with the scaffolding code, it is ready to be imported and used by an app.
The following section guides you through the creation of a Fabric Native Component, step-by-step, targeting the latest version of React Native.
Fabric Native Components only works with the New Architecture enabled. To migrate to the New Architecture, follow the Migration guide
How to Create a Fabric Native Componentsβ
To create a Fabric Native Component, you have to follow these steps:
- Define a set of JavaScript specifications.
- Configure the component so that Codegen can create the shared code and it can be added as a dependency for an app.
- Write the required native code.
Once these steps are done, the component is ready to be consumed by an app. The guide shows how to add it to an app by leveraging autolinking, and how to reference it from the JavaScript code.
1. Folder Setupβ
In order to keep the component decoupled from the app, it's a good idea to define the module separately from the app and then add it as a dependency to your app later. This is also what you'll do for writing Fabric Native Component that can be released as open-source libraries later.
For this guide, you are going to create a Fabric Native Component that centers some text on the screen.
Create a new folder at the same level of the app and call it RTNCenteredText
.
In this folder, create three subfolders: js
, ios
, and android
.
The final result should look like this:
.
βββ MyApp
βββ RTNCenteredText
βββ android
βββ ios
βββ js
2. JavaScript Specificationβ
The New Architecture requires interfaces specified in a typed dialect of JavaScript (either Flow or TypeScript). Codegen uses these specifications to generate code in strongly-typed languages, including C++, Objective-C++, and Java.
There are two requirements the file containing this specification must meet:
- The file must be named
<MODULE_NAME>NativeComponent
, with a.js
or.jsx
extension when using Flow, or a.ts
, or.tsx
extension when using TypeScript. Codegen only looks for files matching this pattern. - The file must export a
HostComponent
object.
Below are specifications of the RTNCenteredText
component in both Flow and TypeScript. Create a RTNCenteredTextNativeComponent
file with the proper extension in the js
folder.
- TypeScript
- Flow
// @flow strict-local
import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{|
...ViewProps,
text: ?string,
// add other props here
|}>;
export default (codegenNativeComponent<NativeProps>(
'RTNCenteredText',
): HostComponent<NativeProps>);
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
export interface NativeProps extends ViewProps {
text?: string;
// add other props here
}
export default codegenNativeComponent<NativeProps>(
'RTNCenteredText',
) as HostComponent<NativeProps>;
At the beginning of the spec files, there are the imports. The most important imports, required by every Fabric Native Component are:
- The
HostComponent
: type the exported component needs to conform to. - The
codegenNativeComponent
function: responsible to actually register the component in the JavaScript runtime.
The second section of the files contains the props of the component. Props (short for "properties") are component-specific information that let you customize React components. In this case, you want to control the text
property of the component.
Finally, the spec file exports the returned value of the codegenNativeComponent
generic function, invoked passing the name of the component.
The JavaScript files imports types from libraries, without setting up a proper node module and installing its dependencies. The outcome of this is that the IDE may have troubles resolving the import statements and it can output errors and warnings. These will disappear as soon as the Fabric Native Component is added as a dependency of a React Native app.
3. Component Configurationβ
Next, you need to add some configuration for Codegen and auto-linking.
Some of these configuration files are shared between iOS and Android, while the others are platform-specific.
Sharedβ
The shared configuration is a package.json
file that will be used by yarn when installing your module. Create the package.json
file in the root of the RTNCenteredText
directory.
{
"name": "rtn-centered-text",
"version": "0.0.1",
"description": "Showcase a Fabric Native Component with a centered text",
"react-native": "js/index",
"source": "js/index",
"files": [
"js",
"android",
"ios",
"rtn-centered-text.podspec",
"!android/build",
"!ios/build",
"!**/__tests__",
"!**/__fixtures__",
"!**/__mocks__"
],
"keywords": ["react-native", "ios", "android"],
"repository": "https://github.com/<your_github_handle>/rtn-centered-text",
"author": "<Your Name> <your_email@your_provider.com> (https://github.com/<your_github_handle>)",
"license": "MIT",
"bugs": {
"url": "https://github.com/<your_github_handle>/rtn-centered-text/issues"
},
"homepage": "https://github.com/<your_github_handle>/rtn-centered-text#readme",
"devDependencies": {},
"peerDependencies": {
"react": "*",
"react-native": "*"
},
"codegenConfig": {
"name": "RTNCenteredTextSpecs",
"type": "components",
"jsSrcsDir": "js"
}
}
The upper part of the file contains some descriptive information like the name of the component, its version and its source files. Make sure to update the various placeholders which are wrapped in <>
: replace all the occurrences of the <your_github_handle>
, <Your Name>
, and <your_email@your_provider.com>
tokens.
Then there are the dependencies for this package. For this guide, you need react
and react-native
.
Finally, the Codegen configuration is specified by the codegenConfig
field. It contains an array of libraries, each of which is defined by three other fields:
name
: The name of the library. By convention, you should add theSpec
suffix.type
: The type of module contained by this package. In this case, it is a Fabric Native Component, thus the value to use iscomponents
.jsSrcsDir
: the relative path to access thejs
specification that is parsed by Codegen.
iOS: Create the .podspec
fileβ
For iOS, you'll need to create a rtn-centered-text.podspec
file which will define the module as a dependency for your app. It will stay in the root of RTNCenteredText
, alongside the ios
folder.
The file will look like this:
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = "rtn-centered-text"
s.version = package["version"]
s.summary = package["description"]
s.description = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.platforms = { :ios => "11.0" }
s.author = package["author"]
s.source = { :git => package["repository"], :tag => "#{s.version}" }
s.source_files = "ios/**/*.{h,m,mm,swift}"
install_modules_dependencies(s)
end
The .podspec
file has to be a sibling of the package.json
file, and its name is the one we set in the package.json
's name
property: rtn-centered-text
.
The first part of the file prepares some variables that we use throughout the file. Then, there is a section that contains some information used to configure the pod, like its name, version, and description.
All the requirements for the New Architecture have been encapsulated in the install_modules_dependencies
. It takes care of installing the proper dependencies based on which architecture is currently enabled. It also automatically installs the React-Core
dependency in the old architecture.