website/versioned_docs/version-0.84/fabric-native-components.md
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import constants from '@site/core/TabsConstants'; import {FabricNativeComponentsAndroid,FabricNativeComponentsIOS} from './_fabric-native-components';
If you want to build new React Native Components that wrap around a Host Component like a unique kind of CheckBox on Android, or a UIButton on iOS, you should use a Fabric Native Component.
This guide will show you how to build Fabric Native Components, by implementing a web view component. The steps to doing this are:
You're going to need a plain template generated application to use the component:
npx @react-native-community/cli@latest init Demo --install-pods false
This guide will show you how to create a Web View component. We will be creating the component by using the Android's WebView component, and the iOS WKWebView component.
Let's start by creating the folders structure to hold our component's code:
mkdir -p Demo/{specs,android/app/src/main/java/com/webview}
This gives you the following layout where you'll working:
Demo
├── android/app/src/main/java/com/webview
└── ios
└── specs
android/app/src/main/java/com/webview folder is the folder that will contain our Android code.ios folder is the folder that will contain our iOS code.specs folder is the folder that will contain the Codegen's specification file.Your specification must be defined in either TypeScript or Flow (see Codegen documentation for more details). This is used by Codegen to generate the C++, Objective-C++ and Java to connect your platform code to the JavaScript runtime that React runs in.
The specification file must be named <MODULE_NAME>NativeComponent.{ts|js} to work with Codegen. The suffix NativeComponent is not only a convention, it is actually used by Codegen to detect a spec file.
Use this specification for our WebView Component:
<Tabs groupId="language" queryString defaultValue={constants.defaultJavaScriptSpecLanguage} values={constants.javaScriptSpecLanguages}> <TabItem value="typescript">import type {
CodegenTypes,
HostComponent,
ViewProps,
} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};
export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}
export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;
// @flow strict-local
import type {CodegenTypes, HostComponent, ViewProps} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = $ReadOnly<{|
result: "success" | "error",
|}>;
type NativeProps = $ReadOnly<{|
...ViewProps,
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent>?;
|}>;
export default (codegenNativeComponent<NativeProps>(
'CustomWebView',
): HostComponent<NativeProps>);
This specification is composed of three main parts, excluding the imports:
WebViewScriptLoadedEvent is a supporting data type for the data the event needs to pass from native to JavaScript.NativeProps is a definition of the props that we can set on the component.codegenNativeComponent statement allows us to codegenerate the code for the custom component and that defines a name for the component used to match the native implementations.As with Native Modules, you can have multiple specification files in the specs/ directory. For more information about the types you can use, and the platform types these map to, see the appendix.
The specification is used by the React Native's Codegen tools to generate platform specific interfaces and boilerplate for us. To do this, Codegen needs to know where to find our specification and what to do with it. Update your package.json to include:
"start": "react-native start",
"test": "jest"
},
// highlight-start
"codegenConfig": {
"name": "AppSpec",
"type": "components",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.webview"
},
"ios": {
"componentProvider": {
"CustomWebView": "RCTWebView"
}
}
},
// highlight-end
"dependencies": {
With everything wired up for Codegen, we need to prepare our native code to hook into our generated code.
Note that for iOS, we are declaratively mapping the name of the JS component that is exported by the spec (CustomWebView) with the iOS class that will implement the component natively.
Now it's time to write the native platform code so that when React requires to render a view, the platform can create the right native view and can render it on screen.
You should work through both the Android and iOS platforms.
:::note This guide shows you how to create a Native Component that only works with the New Architecture. If you need to support both the New Architecture and the Legacy Architecture, please refer to our backwards compatibility guide.
:::
<Tabs groupId="platforms" queryString defaultValue={constants.defaultPlatform}> <TabItem value="android" label="Android"> <FabricNativeComponentsAndroid /> </TabItem> <TabItem value="ios" label="iOS"> <FabricNativeComponentsIOS /> </TabItem> </Tabs>Finally, you can use the new component in your app. Update your generated App.tsx to:
import React from 'react';
import {Alert, StyleSheet, View} from 'react-native';
import WebView from './specs/WebViewNativeComponent';
function App(): React.JSX.Element {
return (
<View style={styles.container}>
<WebView
sourceURL="https://react.dev/"
style={styles.webview}
onScriptLoaded={() => {
Alert.alert('Page Loaded');
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
alignContent: 'center',
},
webview: {
width: '100%',
height: '100%',
},
});
export default App;
This code creates an app that uses the new WebView component we created to load the react.dev website.
The app also shows an alert when the web page is loaded.
| Android | iOS |
|---|---|