react-native Documentation

A framework for building native applications using React

RAW Doc

Technical Documentation: react/react-native

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

[Getting Started](https://reactnative.dev/docs/environment-setup) · [Learn the Basics](https://reactnative.dev/docs/tutorial) · [Showcase](https://reactnative.dev/showcase) · [Contribute](https://reactnative.dev/docs/contributing) · [Community](https://reactnative.dev/help) · [Support](./.github/SUPPORT.md)

React Native

React Native lets you build native apps using React. Written
in JavaScript, rendered with native code.

  • Native UI. React Native primitives render to native platform UI, meaning your app
    uses the same native platform APIs other apps do. Gestures, text scaling, and
    accessibility behave the way users expect on each OS.
  • React, everywhere. Declarative UI, components, hooks, and Suspense, reused
    across Android, iOS, and other platforms.
  • Developer Velocity. See local changes in seconds. Changes to JavaScript code are applied with Fast Refresh, without rebuilding the native app.
  • Extend it yourself. Native Modules let you call platform code directly from JavaScript, synchronously and type-safe — or reach for thousands of existing libraries.

React Native is developed and supported by many companies and individual core contributors. Find out more on the React Foundation website.

Building your first React Native app

Follow the Getting Started guide for a new app, or Integration with Existing Apps to adopt React Native incrementally.

Using a Framework

We believe that the best way to experience React Native is through a Framework, a toolbox with all the necessary APIs to let you build production ready apps. Expo is a production-grade React Native Framework, with file-based routing, a standard library of native modules, and much more.

To create a new Expo project, run the following in your terminal:

text
npx create-expo-app@latest

Then follow the rest of Expo's getting started guide to start building.

Without a Framework

You can also use React Native without a Framework, however we've found that most developers benefit from one — navigation, native dependencies, and platform tooling are problems the ecosystem has already solved. If a Framework doesn't suit your app, follow Getting Started Without a Framework.

Documentation

The full documentation for React Native can be found on our website.

The source for the React Native docs and website is hosted on a separate repository, react/react-native-website.

Contributing

The main purpose of this repository is to continue evolving React Native core. We want to make contributing to this project as easy and transparent as possible, and we are grateful to the community for contributing bug fixes and improvements. Read below to learn how you can take part in improving React Native.

[Code of Conduct](https://code.fb.com/codeofconduct/)

Meta has adopted a Code of Conduct that we expect project participants to adhere to.
Please read the full text so that you can understand what actions will and will not be tolerated.

[Contributing Guide](https://reactnative.dev/docs/contributing)

Read our Contributing Guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React Native.

Discussions

Larger discussions and proposals are discussed in react-native-community/discussions-and-proposals.

React Native releases are discussed in reactwg/react-native-releases.

License

React Native is MIT licensed, as found in the LICENSE file.


Overview (`__docs__/README.md`)

React Native Technical Documentation

The React Native technical documentation describes how React Native works
internally, the subsystems it is composed of, how they work and how they
interact with each other.

The intended audience is people who want to learn about the internals of React
Native and contribute to it. End users of React Native are meant to use the
public website instead
(its code can be found
here).

For details on how we approach technical documentation in this repository, see
GUIDELINES.md.

🚀 Usage

This repository is not meant to be consumed directly by end users. Instead, it
creates several packages that are published to the NPM registry for direct
consumption by end users and frameworks.

This repository uses a monorepo approach, and public packages can be found in
the packages directory (the ones that do not contain
"private": true in their package.json file).

The most important package is the
react-native package, located in
packages/react-native, which contains the public
JavaScript API.

This repository provides the Android and iOS versions of React Native. Versions
for other platforms are maintained in their own repositories.

📐 Design

TODO: Explain the different components of React Native at a high level.

🔗 Relationship with other systems

Part of this

Used by this

This repository has many different types of dependencies: build systems,
external packages to be used during development, external packages used at
runtime, etc.

Uses this

The main use cases for this repository are:

  1. Developing React Native itself.
  2. Testing and releasing React Native.
  3. Synchronizing forks like react-native-windows and react-native-macos.

Overview (`packages/asset-utils/README.md`)

@react-native/asset-utils

[ [

Android resource-path helpers used when copying React Native assets into drawable-* / raw folders. Consumed by bundling and build tooling; most apps never import this directly.

API

js
import {
  getAndroidResourceFolderName,
  getAndroidResourceIdentifier,
} from '@react-native/asset-utils';
Export Signature Notes
getAndroidResourceFolderName (asset: PackagerAsset, scale: number) => string e.g. drawable-xhdpi; non-drawable types resolve to raw
getAndroidResourceIdentifier (asset: PackagerAsset) => string Sanitised resource name
drawableFileTypes Set<string> Asset types that map to a drawable-* folder

Overview (`packages/babel-plugin-codegen/README.md`)

@react-native/babel-plugin-codegen

[ [

Babel plugin that generates native module and view manager binding code for React Native, powered by @react-native/codegen.


Overview (`packages/community-cli-plugin/README.md`)

@react-native/community-cli-plugin

[ [

This is an internal dependency of React Native. Please don't depend on it directly.

CLI entry points supporting core React Native development features.

Formerly @react-native-community/cli-plugin-metro.

Commands

`start`

Start the React Native development server.

Usage

sh
npx @react-native-community/cli start [options]

Options

Option Description
--port <number> Set the server port.
--host <string> Set the server host.
--projectRoot Set the path to the project root.
--watchFolders <list> Specify additional folders to be added to the watch list.
--assetPlugins <list> Specify additional asset plugins.
--sourceExts <list> Specify additional source extensions to bundle.
--max-workers <number> Set the maximum number of workers the worker-pool will spawn for transforming files. Defaults to the number of the cores available on your machine.
--transformer <string> Specify a custom transformer.
--reset-cache Remove cached files.
--custom-log-reporter-path <string> Specify a module path exporting a replacement for TerminalReporter.
--https Enable HTTPS connections.
--key Specify path to a custom SSL key.
--cert Specify path to a custom SSL cert.
--config <string> Path to the CLI configuration file.
--no-interactive Disable interactive mode.
--client-logs [Deprecated] Enable plain text JavaScript log streaming for all connected apps.

`bundle`

Build the bundle for the provided JavaScript entry file.

Usage

sh
npx @react-native-community/cli bundle --entry-file <path> [options]

Options

Option Description
--entry-file Set the path to the root JavaScript entry file.
--platform <string> Set the target platform (either "android" or "ios"). Defaults to "ios".
--transformer <string> Specify a custom transformer.
--dev [boolean] If false, warnings are disabled and the bundle is minified. Defaults to true.
--minify [boolean] Allows overriding whether bundle is minified. Defaults to false if --dev is set. Disabling minification can be useful for speeding up production builds for testing purposes.
--bundle-output <string> Specify the path to store the resulting bundle.
--bundle-encoding <string> Specify the encoding for writing the bundle (https://nodejs.org/api/buffer.html#buffer_buffer).
--resolver-option <string...> Custom resolver options of the form key=value. URL-encoded. May be specified multiple times.
--sourcemap-output <string> Specify the path to store the source map file for the resulting bundle.
--sourcemap-sources-root <string> Set the root path for source map entries.
--sourcemap-use-absolute-path Report SourceMapURL using its full path.
--max-workers <number> Set the maximum number of workers the worker-pool will spawn for transforming files. Defaults to the number of the cores available on your machine.
--assets-dest <string> Specify the directory path for storing assets referenced in the bundle.
--reset-cache Remove cached files.
--read-global-cache Attempt to fetch transformed JS code from the global cache, if configured. Defaults to false.
--config <string> Path to the CLI configuration file.

`codegen`

Run the React Native codegen, generating native boilerplate from JS spec files.

Usage

sh
npx @react-native-community/cli codegen [options]

Options

Option Description
--path Path to the React Native project root. Defaults to the current working directory.
--platform <string> Target platform. Supported values: "android", "ios", "all". Defaults to "all".
--outputPath Path where generated artifacts will be output to.
--source <string> Whether the script is invoked from an app or a library. Defaults to "app".

`spm [action]`

Set up or maintain Swift Package Manager support for the iOS/macOS app. Actions: add, update, deinit, scaffold. With no action: add (or update if SPM is already set up).

Usage

sh
npx @react-native-community/cli spm [action] [options]

Options

Option Description
--version <string> React Native version (e.g. 0.80.0). Defaults to the version in node_modules/react-native/package.json.
--yes Skip the dirty-pbxproj confirmation prompt.
--xcodeproj [add] Path to the .xcodeproj to inject SPM packages into (disambiguates when several exist).
--productName <string> [add] App target to inject into (disambiguates when several exist).
--deintegrate [add] Run pod deintegrate and strip React Native from the Podfile before injecting (CocoaPods → SwiftPM migration).
--artifacts [advanced] Local artifact root containing complete debug/ and release/ slots.
--download <string> [advanced] Artifact download policy: auto (default), skip, or force.
--skipCodegen [advanced] Skip the react-native codegen step.

Contributing

Changes to this package can be made locally and tested against the rn-tester app, per the Contributing guide. During development, this package is automatically run from source with no build step.


Overview (`packages/debugger-frontend/README.md`)

@react-native/debugger-frontend

[ [

Debugger frontend for React Native based on Chrome DevTools. It is intended to be used via @react-native/dev-middleware.

Usage

The package exports the absolute path to the directory containing the frontend assets.

js
const frontendPath = require('@react-native/debugger-frontend');

// Pass frontendPath to a static server, etc

Contributing

Source repo

Source code for this package lives in the react/react-native-devtools-frontend repo. See below for how we build and check in changes.

Updating the frontend assets

The compiled assets for the debugger frontend are periodically checked into this package under the dist/ folder. To update these, run node scripts/debugger-frontend/sync-and-build from the root of your react-native checkout.

sh
# For main
node scripts/debugger-frontend/sync-and-build --branch main

# For stable branches (e.g. '0.73-stable')
node scripts/debugger-frontend/sync-and-build --branch 0.73-stable

By default, this will clone and build from react/react-native-devtools-frontend.

The updated files are committed on completion, with a generated summary and changelog of the synced revisions.


Overview (`packages/debugger-shell/README.md`)

@react-native/debugger-shell

[ [

Experimental Electron-based shell for React Native DevTools.

Why Electron?

The React Native DevTools frontend is based on Chrome DevTools, which is a web app, but is not particularly portable: it's designed to run in Chromium, and Chromium only. Prior to @react-native/debugger-shell, we would run it in hosted mode in an instance of Chrome or Edge.

Relying on hosted mode presents a variety of UX issues in the debugging workflow, such as the need to ask developers to install a particular browser before they can debug in React Native, and the inability to foreground/reuse existing debugger windows when relaunching the debugger for the same app. In order to address these issues effectively, we fundamentally need to leave the browser sandbox and run the debugger in a shell we can bundle with React Native, and whose behavior we can control.

Electron is a tried-and-tested framework for the specific task of embedding a Chromium browser in a portable, customized shell. As a rule we'll hold a high bar for performance and reliability, and we'll only add features to the shell if they are strictly necessary to complement the DevTools frontend's built-in capabilities.


Overview (`packages/dev-middleware/README.md`)

@react-native/dev-middleware

[ [

Dev server middleware supporting core React Native development features. This package is preconfigured in all React Native projects.

Usage

Middleware can be attached to a dev server (e.g. Metro) using the createDevMiddleware API.

js
import { createDevMiddleware } from '@react-native/dev-middleware';

function myDevServerImpl(args) {
  ...

  const {middleware, websocketEndpoints} = createDevMiddleware({
    projectRoot: metroConfig.projectRoot,
    serverBaseUrl: new URL(`http://${args.host}:${args.port}`),
    logger,
  });

  await Metro.runServer(metroConfig, {
    host: args.host,
    ...,
    unstable_extraMiddleware: [
      middleware,
      // Optionally extend with additional HTTP middleware
    ],
    websocketEndpoints: {
      ...websocketEndpoints,
      // Optionally extend with additional WebSocket endpoints
    },
  });
}

Included middleware

@react-native/dev-middleware is designed for integrators such as @expo/dev-server and @react-native/community-cli-plugin. It provides a common default implementation for core React Native dev server responsibilities.

We intend to keep this to a narrow set of functionality, based around:

  • Debugging — The Chrome DevTools protocol (CDP) endpoints supported by React Native, including the Inspector Proxy, which facilitates connections with multiple devices.
  • Dev actions — Endpoints implementing core Dev Menu actions, e.g. reloading the app, opening the debugger frontend.

HTTP endpoints

DevMiddlewareAPI.middleware

These are exposed as a connect middleware handler, assignable to Metro.runServer or other compatible HTTP servers.

GET `/json/list`, `/json` ([CDP](https://chromedevtools.github.io/devtools-protocol/#endpoints))

Returns the list of available WebSocket targets for all connected React Native app sessions.

GET `/json/version` ([CDP](https://chromedevtools.github.io/devtools-protocol/#endpoints))

Returns version metadata used by Chrome DevTools.

GET `/debugger-frontend`

Subpaths of this endpoint are reserved to serve the JavaScript debugger frontend.

POST `/open-debugger`

Open the JavaScript debugger for a given CDP target. Must be provided with one of the following query params:

  • device‌ — An ID unique to a combination of device and app, stable across installs. Implemented by getInspectorDeviceId on each native platform.
  • target — The target page ID as returned by /json/list for the current dev server session.
  • appId (deprecated, legacy only) — The application bundle identifier to match (non-unique across multiple connected devices). This param will only match legacy Hermes debugger targets.

Example

text
curl -X POST 'http://localhost:8081/open-debugger?target=<targetId>'

WebSocket endpoints

DevMiddlewareAPI.websocketEndpoints

`/inspector/device`

WebSocket handler for registering device connections.

`/inspector/debug`

WebSocket handler that proxies CDP messages to/from the corresponding device.

Experimental features

React Native frameworks may pass an unstable_experiments option to createDevMiddleware to configure experimental features. Note that these features might not work correctly, and they may change or be removed in the future without notice. Some of the experiment flags available are documented below.

`unstable_experiments.enableStandaloneFuseboxShell`

Enables launching the debugger frontend in a standalone app shell (provided by the @react-native/debugger-shell package) rather than in a browser window. Since React Native 0.83 this defaults to true, and may be disabled by explicitly passing false.

The shell is powered by a separate binary that is downloaded and cached in the background (immediately after the call to createDevMiddleware). If there is a problem downloading or invoking this binary for the first time, the debugger frontend will revert to launching in a browser window until the next time createDevMiddleware is called (typically, on the next dev server start).

Contributing

Changes to this package can be made locally and tested against the rn-tester app, per the Contributing guide. During development, this package is automatically run from source with no build step.


Overview (`packages/eslint-config-react-native/README.md`)

@react-native/eslint-config

[ [

ESLint and Prettier configuration used by React Native apps.

Installation

sh
yarn add --dev eslint prettier @react-native/eslint-config

Usage

For ESLint 9+ (Flat Config)

Add to your eslint.config.js:

javascript
const reactNativeConfig = require('@react-native/eslint-config/flat');

module.exports = [
  ...reactNativeConfig,
  // Your custom config here
];

Or with ES modules:

javascript
import reactNativeConfig from '@react-native/eslint-config/flat';

export default [
  ...reactNativeConfig,
  // Your custom config here
];

For ESLint 8 (Legacy Config)

Add to your eslint config (.eslintrc, or eslintConfig field in package.json):

json
{
    "extends": "@react-native"
}

Overview (`packages/eslint-plugin-react-native/README.md`)

@react-native/eslint-plugin

[ [

ESLint rules for @react-native/eslint-config. You probably want to install that package instead.

Rules

`platform-colors`

Enforces that calls to PlatformColor and DynamicColorIOS are statically analyzable to enable performance optimizations.


Overview (`packages/eslint-plugin-specs/README.md`)

@react-native/eslint-plugin-specs

[ [

ESLint rules that validate React Native Native Module and Component specs.


Overview (`packages/gradle-plugin/README.md`)

@react-native/gradle-plugin

[ [

Gradle plugin used to build and configure React Native applications for Android. It is applied automatically by apps created from the React Native template.


Overview (`packages/jest-preset/README.md`)

@react-native/jest-preset

[ [

Jest preset for React Native apps.

Usage

Installation

Install @react-native/jest-preset in your app:

with npm:

sh
npm i @react-native/jest-preset --save-dev

or with yarn:

sh
yarn add -D @react-native/jest-preset

Configuring Jest

Then, create a file called jest.config.js in your project's root directory. Then load this preset:

text
module.exports = {
  preset: '@react-native/jest-preset',
};

You can further customize your Jest configuration by specifying other options. See Jest's jest.config.js documentation to learn more.


Overview (`packages/metro-config/README.md`)

@react-native/metro-config

[ [

Metro configuration for React Native. In React Native, your Metro config should extend either @react-native/metro-config or @expo/metro-config. These packages contain essential defaults necessary to build and run React Native apps.

See Configuring Metro for the full guide.

Usage

js
const {
  getDefaultConfig,
  mergeConfig,
} = require('@react-native/metro-config');

/**
 * Metro configuration
 * https://metrobundler.dev/docs/configuration
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

Overview (`packages/new-app-screen/README.md`)

@react-native/new-app-screen

[ [

NewAppScreen component for React Native.

Usage

js
import { NewAppScreen } from '@react-native/new-app-screen';

function MyAppOrTemplate() {
  ...

  return <NewAppScreen />;
}

Contributing

Changes to this package can be made locally and tested against the rn-tester app, per the Contributing guide.


Overview (`packages/normalize-color/README.md`)

@react-native/normalize-colors

[ [

Color normalization utility for React Native. Converts CSS color values into the numeric form consumed by the native layer.


Overview (`packages/polyfills/README.md`)

@react-native/js-polyfills

[ [

This is an internal dependency of React Native. Please don't depend on it directly.

JavaScript environment polyfills set up by React Native at startup (e.g. Promise, timers, and other runtime globals).


Overview (`packages/react-native-babel-preset/README.md`)

@react-native/babel-preset

[ [

Babel preset for React Native applications. React Native uses this Babel preset by default when transforming your app's source code.

You only need to use this preset directly if you provide a custom babel.config.js file in your project's root directory. React Native does not apply its default Babel configuration in that case, so you must specify all the plugins necessary to transform your code — start from this preset to get the defaults and add more plugins on top.

Usage

Installation

Install @react-native/babel-preset in your app:

with npm:

sh
npm i @react-native/babel-preset --save-dev

or with yarn:

sh
yarn add -D @react-native/babel-preset

Configuring Babel

Then, create a file called babel.config.js in your project's root directory. The existence of this babel.config.js file tells React Native to use your custom Babel configuration instead of its own. Then load this preset:

json
{
  "presets": ["module:@react-native/babel-preset"]
}

You can further customize your Babel configuration by specifying plugins and other options. See Babel's babel.config.js documentation to learn more.


Overview (`packages/react-native-babel-transformer/README.md`)

@react-native/metro-babel-transformer

[ [

Metro Babel transformer for React Native applications. Applies @react-native/babel-preset when transforming source files during bundling.


Overview (`packages/react-native-codegen/README.md`)

@react-native/codegen

[ [

Code generation tools for React Native. Parses TypeScript and Flow NativeModule and Component specs and generates the native binding code used by the New Architecture.


Overview (`packages/react-native-compatibility-check/README.md`)

@react-native/compatibility-check

[ [

Status: Experimental (stage 1)

Work In Progress. Documentation is lacking, and intended to be used by power
users at this point.

This tool enables checking the boundary between JavaScript and Native for
backwards incompatible changes to protect against crashes.

This is useful for:

  • Local Development
  • Over the Air updates on platforms that support it
  • Theoretically: Server Components with React Native

**Motivating Problems**

Let’s look at some motivating examples for this project.

[!NOTE]
The examples below are written with Flow, but the compatibility-check
tool is agnostic to the types you write. The compatibility-check runs on JSON
schema files, most commonly generated by the
@react-native/codegen
tool which supports both TypeScript and Flow.

**Adding new methods**

You might have an Analytics Native Module in your app, and you last built the
native client a couple of days ago:

javascript
export interface Spec extends TurboModule {
  log: (eventName: string, content: string) => void;
}

And you are working on a change to add a new method to this Native Module:

javascript
export interface Spec extends TurboModule {
  log: (eventName: string, content: string) => void;
  logError: (message: string) => void;
}
text
NativeAnalytics.logError('Oh No! We hit a crash')

Since you are working on this, you’ve built a new native client and tested the
change on your computer and everything works.

However, when your colleague pulls your latest changes and tries to run it,
they’ll get a crash logError is not a function. They need to rebuild their
native client!

Using this tool, you can detect this incompatibility at build time, getting an
error that looks like:

text
NativeAnalytics: Object added required properties, which native will not provide
  -- logError

Errors like this can occur for much more nuanced reasons than adding a method.
For example:

**Sending native new union values**

javascript
export interface Spec extends TurboModule {
  // You add 'system' to this union
  +setColorScheme: (color: 'light' | 'dark') => void;
}

If you add a new option of system and add native support for that option, when
you call this method with system on your commit it would work but on an older
build not expecting system it will crash. This tool will give you the error
message:

text
ColorManager.setColorScheme parameter 0: Union added items, but native will not expect/support them
  -- position 3 system

**Changing an enum value sent from native**

As another example, say you are getting the color scheme from the system as an
integer value, used in JavaScript as an enum:

javascript
enum TestEnum {
  LIGHT = 1,
  DARK = 2,
  SYSTEM = 3,
}

export interface Spec extends TurboModule {
  getColorScheme: () => TestEnum;
}

And you realize you actually need native to send -1 for System instead of 3.

javascript
enum TestEnum {
  LIGHT = 1,
  DARK = 2,
  SYSTEM = -1,
}

If you make this change and run the JavaScript on an old build, it might still
send JavaScript the value 3, which your JavaScript isn’t handling anymore!

This tool gives an error:

javascript
ColorManager: Object contained a property with a type mismatch
   -- getColorScheme: has conflicting type changes
       --new: ()=>Enum<number>
       --old: ()=>Enum<number>
       Function return types do not match
           --new: ()=>Enum<number>
           --old: ()=>Enum<number>
           Enum types do not match
               --new: Enum<number> {LIGHT = 1, DARK = 2, SYSTEM = -1}
               --old: Enum<number> {LIGHT = 1, DARK = 2, SYSTEM = 3}
               Enum contained a member with a type mismatch
                   -- Member SYSTEM: has conflicting changes
                       --new: -1
                       --old: 3
                       Numeric literals are not equal
                           --new: -1
                           --old: 3

**Avoiding Breaking Changes**

You can use this tool to either detect changes locally to warn that you need to
install a new native build, or when doing OTA you might need to guarantee that
the changes in your PR are compatible with the native client they’ll be running
in.

**Example 1**

In example 1, when adding logError, it needs to be optional to be safe:

javascript
export interface Spec extends TurboModule {
  log: (eventName: string, content: string) => void;
  logError?: (message: string) => void;
}

That will enforce if you are using TypeScript or Flow that you check if the
native client supports logError before calling it:

javascript
if (NativeAnalytics.logError) {
  NativeAnalytics.logError('Oh No! We hit a crash');
}

**Example 2**

When you want to add 'system' as a value to the union, modifying the existing
union is not safe. You would need to add a new optional method that has that
change. You can clean up the old method when you know that all of the builds you
ever want to run this JavaScript on have native support.

javascript
export interface Spec extends TurboModule {
  +setColorScheme: (color: 'light' | 'dark') => void
  +setColorSchemeWithSystem?: (color: 'light' | 'dark' | 'system') => void
}

**Example 3**

Changing a union case is similar to Example 2, you would either need a new
method, or support the existing value and the new -1.

text
enum TestEnum {
  LIGHT = 1,
  DARK = 2,
  SYSTEM = 3,
  SYSTEM_ALSO = -1,
}

**Installation**

text
yarn add @react-native/compatibility-check

**Usage**

To use this package, you’ll need a script that works something like this:

This script checks the compatibility of a React Native app's schema between two
versions. It takes into account the changes made to the schema and determines
whether they are compatible or not.

javascript
import {compareSchemas} from '@react-native/compatibility-check';
const util = require('util');

async function run(argv: Argv, STDERR: string) {
  const debug = (log: mixed) => {
    argv.debug &&
      console.info(util.inspect(log, {showHidden: false, depth: null}));
  };

  const currentSchema =
    JSON.parse(/*you'll read the file generated by codegen wherever it is in your app*/);
  const previousSchema =
    JSON.parse(/*you'll read the schema file that you persisted from when your native app was built*/);

  const safetyResult = compareSchemas(currentSchema, previousSchema);

  const summary = safetyResult.getSummary();
  switch (summary.status) {
    case 'ok':
      debug('No changes in boundary');
      console.log(JSON.stringify(summary));
      break;
    case 'patchable':
      debug('Changes in boundary, but are compatible');
      debug(result.getDebugInfo());
      console.log(JSON.stringify(summary));
      break;
    default:
      debug(result.getDebugInfo());
      console.error(JSON.stringify(result.getErrors()));
      throw new Error(`Incompatible changes in boundary`);
  }
}

Overview (`packages/react-native-popup-menu-android/README.md`)

@react-native/popup-menu-android

[ [

PopupMenuAndroid component for React Native, exposing the Android platform's PopupMenu for displaying a menu anchored to a view.


Overview (`packages/react-native/README.md`)

Where is the template?

The React Native Template has moved from being shipping inside the react-native package to it's own package: @react-native-community/template.

Why has it moved?

The React Native Frameworks RFC0759 provides background information.

When was it moved?

The 0.75 release.


Overview (`packages/rn-tester/README.md`)

RNTester

The RNTester showcases React Native views and modules.

Running this app

Before running the app, make sure you ran:

sh
git clone https://github.com/facebook/react-native.git
cd react-native
yarn install

Running on iOS

If you are testing non-fabric component, search for and modify the fabric_enabled flag in RNTester's Podfile.

ruby
fabric_enabled = false

Also, if you previously built RNTester with fabric enabled, you might need to clean up the build files and Pods.

sh
# Clean the generated files and folders to clean install RNTester
cd packages/rn-tester
yarn clean-ios

If you are still having a problem after doing the clean up (which can happen if you have built RNTester with older React Native versions where files were generated inside the react-native folder.), the best way might be to clean-install react-native (e.g. remove node_modules and yarn install). If after cleaning up, you are still having issues, you might also want to try RCT_NEW_ARCH_ENABLED=0 bundle exec pod install to disable the new architecture as it might be conflicting.

Both macOS and Xcode are required.

  1. cd packages/rn-tester
  2. Install Bundler: gem install bundler. We use bundler to install the right version of CocoaPods locally.
  3. Install Bundler and CocoaPods dependencies: bundle install && bundle exec pod install or yarn prepare-ios.
  4. Open the generated RNTesterPods.xcworkspace. This is not checked in, as it is generated by CocoaPods. Do not open RNTesterPods.xcodeproj directly.

Note for Apple Silicon users

If you own a Mac laptop with Apple Silicon, you need to run some different commands to install and run cocoapods.

  • sudo arch -x86_64 gem install ffi: this installs the ffi package to load dynamically-linked libraries.
  • arch -x86_64 pod install: this run pod install with the right architecture.

Running on Android

You'll need to have all the prerequisites (SDK, NDK) for Building React Native installed.

You can build and run RN-Tester by using this command from the root of the repo:

sh
yarn android

[!NOTE]
Building for the first time can take a while.

If you're using a physical device, run adb reverse tcp:8081 tcp:8081 to make sure the device can access Metro.

See Running on Device for additional instructions on using a physical device.

Building from source

Building the app on both iOS and Android means building the React Native framework from source. This way you're running the latest native and JS code the way you see it in your clone of the github repo.

This is different from apps created using react-native init which have a dependency on a specific version of React Native JS and native code, declared in a package.json file (and build.gradle for Android apps).

Running tests (iOS)

RNTester provides both integration and unit tests. You can run them all by hitting Cmd+U in Xcode. Alternatively, it is possible to run just unit tests or integration tests using the keybinding after selecting the appropriate scheme (either RNTesterUnitTests or RNTesterIntegrationTests).


Overview (`packages/typescript-config/README.md`)

@react-native/typescript-config

[ [

This package provides the default tsconfig.json used by newly built React Native apps.

This template is customized for specific versions of React Native, and should be updated in sync with the rest of your app.

Opting out of the Strict TypeScript API (default in 0.87)

To opt out of the new Strict TypeScript API you can extend from @react-native/typescript-config/strict

If your app still needs access to deep 'react-native/Libraries/*' imports (deprecated in 0.80), you can opt out via customConditions in your tsconfig.json:

jsonc
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    // ...
    "customConditions": ["react-native", "react-native-legacy-deep-imports"]
  }
}

Overview (`packages/virtualized-lists/README.md`)

@react-native/virtualized-lists

[ [

This is an internal dependency of React Native. Please don't depend on it directly.

Virtualized list primitives (VirtualizedList, VirtualizedSectionList) backing React Native's FlatList and SectionList components.


Overview (`private/core-cli-utils/README.md`)

@react-native/core-cli-utils

A reference implementation of React Native CLI tooling. This package provides composable, ordered Task objects for common CLI operations including Android Gradle builds, iOS Xcode/CocoaPods workflows, Metro bundling with Hermes support, and cache cleaning.

This is not published to npm. Framework authors can use this code as a starting point for their own CLI tooling, but should not depend on it as a versioned API.

Modules

  • android — Gradle-based Android build tasks (assemble, build, install)
  • apple — Xcode/CocoaPods-based iOS tasks (bootstrap, build, install)
  • app — Metro bundler tasks (watch mode, bundle mode, Hermes bytecode compilation)
  • clean — Cache-cleaning tasks (Android/Gradle, Metro, npm, Watchman, Yarn, CocoaPods)
  • version — Semver version requirements for platform toolchains (Android NDK/SDK, Xcode, Node, etc.)

Consumers


Overview (`private/eslint-plugin-monorepo/README.md`)

@react-native/eslint-plugin-monorepo

This is a private package that contains ESLint rules for react-native itself.
They are configured in the .eslintrc.js at the repository root.

For ESLint rules intended for projects that consume React Native, add them to
the @react-native-community/eslint-plugin package instead.


Overview (`private/helloworld/README.md`)

Hello World Test Application

This is the React Native HelloWorld test app. It is for internal use and shouldn't be dependend on.


Overview (`scripts/cxx-api/README.md`)

scripts/cxx-api

Python build pipeline for React Native's C++ (and Objective-C) API snapshots.

Overview

scripts/cxx-api generates human-readable snapshots of React Native's public C++ API surface. It uses Doxygen to parse C/C++/Objective-C headers and a custom Python parser to produce a simplified, sorted representation of every public symbol. Symbols declared in headers that the C++ stable API classifies as private or "for frameworks" are left out by default (see Tier filtering).

The pipeline produces one .api snapshot file per configured API view × variant combination:

Snapshot Description
ReactCommonDebugCxx.api Platform-independent C++ API (debug)
ReactCommonReleaseCxx.api Platform-independent C++ API (release)
ReactAndroidDebugCxx.api Android-specific C++ API (debug)
ReactAndroidReleaseCxx.api Android-specific C++ API (release)
ReactAppleDebugCxx.api Apple-specific C++/Obj-C API (debug)
ReactAppleReleaseCxx.api Apple-specific C++/Obj-C API (release)
ReactCommonFrameworksCxx.api Platform-independent C++ API, including for-frameworks symbols
ReactAndroidFrameworksCxx.api Android-specific C++ API, including for-frameworks symbols
ReactAppleFrameworksCxx.api Apple-specific C++/Obj-C API, including for-frameworks symbols

For each view, debug and release variants are generated with different preprocessor definitions (e.g. REACT_NATIVE_DEBUG vs NDEBUG), since #ifdef guards in the source headers can produce a different public API surface per variant. The frameworks variant uses the view's base definitions and also includes symbols from "for frameworks" headers.

Snapshot files are committed to the repo under scripts/cxx-api/api-snapshots/.

Usage

Generate snapshots

Maintainers should run this command whenever making intentional C++ API changes:

sh
python -m scripts.cxx-api.parser

Validate snapshots against committed baseline

This mode generates snapshots to a temporary directory and compares them against the committed .api files. It is designed for CI:

sh
python -m scripts.cxx-api.parser --validate

If any snapshot differs, a unified diff is printed and the process exits with a non-zero status. To fix a failing validation, regenerate the snapshots with python -m scripts.cxx-api.parser and commit the updated .api files.

Log tier boundary breaks

Pass --log-boundary-breaks (in either mode) to print every header whose includes cross a tier boundary, with the include chain that causes it:

sh
python -m scripts.cxx-api.parser --validate --log-boundary-breaks

How it works

The pipeline has three main stages:

1. Tier classification

Every header in a view's inputs is classified by the C++ stable API guard it includes:

Guard Tier
react/cxxstableapi/UmbrellaGuard.h public
react/cxxstableapi/FrameworksGuard.h for frameworks
react/cxxstableapi/PrivateGuard.h private
none unclassified

The headers' #include/#import directives are resolved into an include graph of the view.

2. Doxygen XML generation

Doxygen is configured via a generated config file (built from .doxygen.config.template) with the input directories, exclude patterns, and preprocessor definitions specified in config.yml. It outputs XML describing every symbol found in the headers.

3. Snapshot parsing

The Python parser (parser/) reads the Doxygen XML output and builds a scope tree of the public API surface, leaving out symbols declared in skipped headers. The tree is then serialized to a deterministically sorted, human-readable .api text format.

Tier filtering

Each view includes the tiers listed in its visibility config (public, frameworks, private), only public by default. A header in any other tier is skipped unless a header in an included tier reaches it, directly or through other includes: anything a public header includes is public in practice, whatever its own guard says. Unclassified headers are never skipped.

A boundary break is a public header that reaches a for-frameworks or private header, or a for-frameworks header that reaches a private one. --log-boundary-breaks reports them at the edge where visibility drops.

When to use it

The snapshot should be regenerated whenever making intentional changes to the public C++ API surface. This includes additions, removals, and changes to files located in:

  • xplat/js/react-native-github/
  • xplat/js/react-native-github/ReactCommon/
  • xplat/js/react-native-github/ReactAndroid/
  • xplat/js/react-native-github/ReactApple/
  • xplat/js/react-native-github/Libraries/

Configuration

All API views and their variants are defined in config.yml. Each view specifies:

Field Description
inputs Directories to scan for headers
exclude_patterns Glob patterns for files to skip
definitions Preprocessor macros to define
variants Named build variants (e.g. debug/release) with extra definitions
codegen Optional codegen platform (android, ios) to generate TurboModule/Component headers before scanning
exclude_symbols Regex patterns for symbols to skip
input_filter Whether to run Doxygen through the input filters in parser/input_filters/
visibility C++ stable API tiers to include (public, frameworks, private); defaults to [public]. See Tier filtering

exclude_patterns and exclude_symbols can also be set at the top level, in which case they apply to every view. A top-level visibility applies to every view that does not set its own, and a variant can set visibility to override its view's.

Snapshot format

The .api files use a minimal pseudo-C++ syntax designed for easy diffing:

text
namespace facebook::react {
  class ComponentDescriptor {
    public ComponentDescriptor(ComponentDescriptorParameters params);
    public ComponentHandle getComponentHandle();
  }

  enum class AccessibilityRole {
    None = 0,
    Button = 1,
  }
}
  • Scopes and members are sorted alphabetically.
  • Access specifiers (public, protected) are preserved.
  • Template parameters are included.
  • Doc comments and source file names are stripped.

Overview (`scripts/js-api/README.md`)

scripts/js-api

TypeScript build pipeline for React Native's JavaScript API.

Overview

yarn build-types is a custom build pipeline for translating React Native's Flow source code to TypeScript.

Specifically, it reduces the runtime JavaScript API of react-native into two outputs:

  • Generated TypeScript types
    Public user types for react-native, shipped to npm
    packages/react-native/types_generated/
  • ‌Public API snapshot
    Snapshot file of the public API shape, used by maintainers
    packages/react-native/ReactNativeApi.d.ts

Dependencies

yarn build-types makes use of the following dependencies, composed with other pre/post transformation steps and dependency resolution.

Usage

Build generated types + API snapshot

Maintainers should run this script whenever making intentional API changes.

sh
# Build types + API snapshot
yarn build-types [--validate]

# Build types without API snapshot
yarn build-types --skip-snapshot

Diff API snapshot compatibility

This script is run by CI to compare changes to ReactNativeApi.d.ts between commits.

sh
# Compare two versions of the API snapshot
yarn js-api-diff <before.d.ts> <after.d.ts>
json
{
  "result": "BREAKING",
  "changedApis": [
    "ViewStyle"
  ]
}

Configuration

Sparse configuration options are defined and documented in scripts/js-api/config.js.

About the two output formats

Generated TypeScript types

types_generated/

Directory providing TypeScript user types for the react-native package, distributed via npm.

  • Gitignored.
  • Scoped to the index.d.ts entry point via package.json#exports.
  • Preserves unstable_ and experimental_ APIs.
  • Preserves doc comments.
  • Preserves source file names (for go to definition).

Public API snapshot

ReactNativeApi.d.ts

Provides a human-readable, maintainable reference of the React Native's public JavaScript API, optimized for developers and diff tooling.

  • Committed to the repo.
  • Strips unstable_ and experimental_ APIs.
  • Strips doc comments.
  • Strips source file names (types are merged into a single program).
  • Versions exported APIs with an 8 char SHA hash, which will be updated when any input type dependencies change shape.

Overview (`scripts/release-testing/README.md`)

scripts/release-testing

Scripts supporting local manual release testing. See also How to Test a Release.

Commands

For information on command arguments, run node <command> --help.

`test-release-local`

Set up, build, and install a given test app configuration.

`test-release-local-clean`

Clean up all file system and cache state between tests.


Overview (`scripts/releases-ci/README.md`)

scripts/releases-ci

CI-only release scripts — intended to run from a CI workflow (GitHub Actions).

Commands

For information on command arguments, run node <command> --help.

`prepare-package-for-release`

Prepares files within the react-native package and template for the target release version. Writes a new commit and tag, which will trigger publish-npm.js in a new workflow.

`publish-npm`

Prepares release artifacts and publishes the react-native package to npm.

`cleanup-maven-staging-repositories`

Drops Sonatype staging repositories that exactly match the description in
ORG_GRADLE_PROJECT_SONATYPE_REPOSITORY_DESCRIPTION, along with any associated
unpublished Central Portal deployments.

`publish-updated-packages`

Publishes all updated packages (excluding react-native) to npm. Triggered when a commit on a release branch contains #publish-packages-to-npm.


Overview (`scripts/releases/README.md`)

scripts/releases

Scripts related to creating a React Native release. These are the lower level entry points used by scripts/releases-ci.

Commands

For information on command arguments, run node <command> --help.

`create-release-commit`

Creates a release commit to trigger a new release.

`set-version`

Bump the version of all packages.

  • Updates package.json metadata for all workspaces and the project root.
  • Updates relevant native files in the react-native package.

If --skipReactNativeVersion is passed, the react-native package version will be left unmodified as "1000.0.0" (special static version on main), and native files will not be touched.

`set-rn-artifacts-version`

[!Note]
Deprecated: Prefer set-version. This entry point is a subset of set-version, and is used only by test workflows. We will replace these call sites in future.

Updates relevant native files in the react-native package to materialize the given release version. This is run by set-version unless --skipReactNativeVersion is passed.


Overview (`packages/gradle-plugin/react-native-gradle-plugin/README.md`)

React Native Gradle Plugin

This plugin is used by React Native Apps to configure themselves.

NOTE: It's important that this folder is called react-native-gradle-plugin as it's used
by users in their build.gradle file as follows:

gradle
buildscript {
    // ...
    dependencies {
        classpath("com.facebook.react:react-native-gradle-plugin")
    }
}

The name of the artifact is imposed by the folder name.


Overview (`packages/react-native/ReactAndroid/README.md`)

Building React Native for Android

See the Building from Source guide on the React Native website.

Running tests

When you submit a pull request, ci will automatically run all tests.
To run tests locally, see Testing guide on the React Native website.


Overview (`packages/react-native/ReactApple/README.md`)

ReactApple

ReactApple contains code to ship React Native apps to Apple devices. The dominant language is Objective-C.

New libraries built with Apple frameworks or intended to ship to Apple devices should live in this directory.


Overview (`private/react-native-fantom/__docs__/README.md`)

👻 React Native Fantom

🏠 Home

[!WARNING]

This is experimental!

We are limiting the scope of the project to just React Native internals for
now, so we can iterate on it quickly and keep the maintenance costs at bay.

In the future, we might explore providing it for testing library/product code
internally and externally.

This means tests must live in packages/react-native.

Fantom is the new integration testing and benchmarking tool for React
Native
.

Its main goal is to allow running JavaScript code as close as possible to a real
React Native application, using its cross-platform architecture (Hermes, Fabric,
C++ TurboModules, Bridgeless, etc.) in a fast headless environment that can run
on CI.

Removing the need for real devices and simulators makes this faster and more
stable than existing e2e testing solutions, while still allowing us to test the
integration between JavaScript and native without the need for mocks.

When compared against Jest, layout is calculated and can be inspected in tests:

javascript
const root = Fantom.createRoot({viewportWidth: 200, viewportHeight: 600});
let viewElement;

Fantom.runTask(() => {
  root.render(
    <View
      ref={node => {
        viewElement = node;
      }}
      style={{width: '50%', height: '10%'}}
    />,
  );
});

// Without Fantom, getBoundingClientRect would have to be mocked.
const boundingClientRect = viewElement.getBoundingClientRect();
expect(boundingClientRect.height).toBe(60);
expect(boundingClientRect.width).toBe(100);

Fantom is designed to make it possible to test the integration between
JavaScript, React and React Native core - platform agnostic parts. When you are
making a change to any of these parts, you should consider writing a Fantom test
for it. It is geared towards engineers working on React Native.

With Fantom you can create scenarios close to those how a real product will
interact with React Native and observe what effects it has on a mock host
platform. It exposes fine grained controls over scheduling, making it possible
to test cases that are hard to reproduce manually.

🚀 Usage

Create a file with the -itest.js suffix anywhere you would normally create a
Jest unit test file.

The high level structure of Fantom tests is similar to Jest unit tests. However,
only a subset of Jest's Global API is currently available. For example,
test.each is not yet implemented in Fantom. We are working on adding more Jest
APIs. If you are blocked by the lack of a specific API, please reach out to us.

Most of the interesting APIs are available via the @react-native/fantom
package. Test files should also import setUpDefaultReactNativeEnvironment at
the top to set up the React Native environment. Do not import
InitializeCore
— it installs LogBox, which interferes with Fantom's error
handling.

javascript
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
import * as Fantom from '@react-native/fantom';

describe('My feature', () => {
  it('should do something interesting', () => {
    const root = Fantom.createRoot();

    Fantom.runTask(() => {
      root.render(/* ... */);
    });

    /* some checks */
  });
});

For a full API reference, please see the inline documentation
defined for the methods in the @react-native/fantom module.

You can check out existing files with the -itest.js suffix (e.g.:
View-itest)
for code examples.

Run the test using the following command from the root of the React Native

shell
yarn fantom <regexForTestFiles>

Similar to Jest, you can also run Fantom in watch mode using --watch:

shell
yarn fantom <regexForTestFiles> --watch

Interactive REPL

[!WARNING]

The REPL is experimental — its behavior and APIs may change.

You can start an interactive REPL that evaluates JavaScript against the same
Hermes runtime and React Native environment that Fantom tests use:

shell
yarn fantom-cli

Each line is evaluated in a persistent session (bindings declared on one line
are available on the next) and goes through Metro, so import, JSX and Flow
syntax all work. React, ReactNative and the Fantom API are exposed as
globals, so you can explore them — and render surfaces — without importing
anything. Results are printed with a console-style inspector (similar to Chrome
DevTools / Node.js), and pressing Tab autocompletes global
identifiers, in-scope bindings and object properties.

text
👻 Fantom REPL — evaluating against Hermes inside the Fantom runtime.
   ⚠️  Experimental: behavior and APIs may change.
fantom> const x = 21
fantom> x * 2
42
fantom> ({items: [1, 2, 3], nested: {ok: true}})
{ items: [ 1, 2, 3 ], nested: { ok: true } }

Press Ctrl+D to exit.

Like Node, you can also evaluate a snippet and exit with -e, or run a script
file:

shell
yarn fantom-cli -e "console.log(Fantom.getHostPlatform())"
yarn fantom-cli path/to/script.js

In both cases the value of a trailing expression is not printed (use
console.log), and a thrown error exits with a non-zero status code.

Timers and asynchronous code in the REPL

The REPL runs in the Fantom environment, which uses a deterministic virtual
clock
— real wall-clock time never passes on its own. A zero-delay
setTimeout(fn, 0) runs right after the current line, but timers with a delay
(and setInterval) stay pending until the clock is advanced, so this does
not print on its own:

text
fantom> setTimeout(() => console.log('hi'), 1000)

Drive timers by installing the timer mock and advancing the clock, exactly like
in a test (see the timers FAQ below):

text
fantom> const timers = Fantom.installTimerMock()
fantom> setTimeout(() => console.log('hi'), 1000)
fantom> timers.advanceTimersByTime(1000)
hi

Conventions

  • Prefer testing React Native through its public API (import from
    'react-native') whenever possible, rather than reaching into internal
    modules or relying on private/protected members (e.g. __-prefixed methods
    and fields such as __getValue()). Exercising the public API also drives the
    underlying native (Fabric/TurboModules) code, so tests stay close to real
    usage and are more resilient to internal refactors. Observe results through
    the public surface too — e.g. assert the rendered output
    (root.getRenderedOutput(...)) or values delivered to public listeners rather
    than reading private state.

    • When Fantom doesn't support something (a native module, a capability, a way
      to observe a result, etc.), it's fine to reach into internals to work around
      that limitation. Prefer a short comment explaining why the internal access
      is necessary.
  • Place test files in __tests__ directories alongside the code being tested.

  • Benchmark tests use the -benchmark-itest.js suffix.

  • Use Fantom.runTask() to render and run synchronous operations; it ensures
    the React work is flushed before assertions.

  • Access elements through refs and use nullthrows for type-safe access to the
    underlying instance:

    javascript
    import nullthrows from 'nullthrows';
    
    const element = nullthrows(elementRef.current);
  • Prefer component-specific instance types (TextInputInstance,
    ScrollViewInstance, etc.) over the generic HostInstance when available.

  • For components with imperative APIs (focus, blur, clear, etc.), test:

    • Each method's behaviour and edge cases (e.g. blur when not focused).
    • Method timing — that it works when called from refs, useLayoutEffect, and
      useEffect.
  • Verify that native commands are dispatched with
    root.takeMountingManagerLogs().

  • Don't write tests with only trivial assertions like
    expect(element).toBeDefined() when more complex behaviour is under test.
    When a test fails, understand what should render rather than weakening the
    assertion to make it pass.

Assertions

Prefer evaluating rendered output inline with .toEqual() and inline JSX over
.toMatchSnapshot() or weak numeric assertions like
element.childNodes.length.

javascript
// Get JSX representation
expect(root.getRenderedOutput().toJSX()).toEqual(
  <rn-view width="100" height="50" />,
);

// Include layout metrics
expect(root.getRenderedOutput({includeLayoutMetrics: true}).toJSX()).toEqual(
  <rn-view
    layoutMetrics-frame="{x:0,y:0,width:100,height:50}"
    layoutMetrics-displayType="Flex"
  />,
);

// Filter to specific props for minimal assertions
expect(root.getRenderedOutput({props: ['backgroundColor']}).toJSX()).toEqual(
  <rn-view backgroundColor="rgba(255, 0, 0, 1)" />,
);

For element-level assertions, get a typed reference and inspect tag names,
layout metrics, or children:

javascript
const elementRef = createRef<HostInstance>();

Fantom.runTask(() => {
  root.render(
    <View ref={elementRef}>
      <Text>the quick brown fox</Text>
    </View>,
  );
});

const element = ensureInstance(elementRef.current, ReactNativeElement);
expect(element.tagName).toBe('RN:View');

const bounds = element.getBoundingClientRect();
expect(bounds.width).toBe(100);
expect(bounds.height).toBe(50);

expect(root.getRenderedOutput().toJSX()).toEqual(
  <rn-view>
    <rn-paragraph>the quick brown fox</rn-paragraph>
  </rn-view>,
);

Limitations

  • Fantom.runTask() calls cannot be nested — doing so will throw.
  • Only a subset of Jest's Global API is available (e.g. test.each is not
    implemented). Reach out if you're blocked by a specific missing API.
  • Tests must live within packages/react-native; Fantom is not currently
    intended for application-specific code.

Test configuration

You can configure certain aspects of the test execution using pragmas in the
docblock at the top of the file. E.g.:

javascript
/**
 * @fantom_flags jsOnlyTestFlag:true
 * @fantom_mode opt
 */

Available pragmas:

  • @fantom_flags: used to set overrides for
    ReactNativeFeatureFlags.
    • Example: @fantom_flags name:value.
    • Multiple flags can be defined in different lines or in the same line
      separated by spaces (e.g.: @fantom_flags name:value otherName:otherValue).
  • @fantom_mode: used to define the compilation mode for the bundle.
    • Example: @fantom_mode opt
    • Possible values:
      • dev: development, default for tests.
      • opt: optimized and using Hermes bytecode, default for benchmarks.
  • @fantom_native_opt: used to define the compilation mode for native code.
    • Example: @fantom_native_opt true
    • Possible values:
      • true: optimized native code
      • false: development native code
  • @fantom_js_opt: used to define the compilation mode for the JS bundle.
    • Example: @fantom_js_opt true
    • Possible values:
      • true: optimized JS bundle
      • false: development JS bundle
  • @fantom_js_bytecode: used to define if the JS bundle should use bytecode.
    • Example: @fantom_js_bytecode true
    • Possible values:
      • true: using Hermes bytecode
      • false: not using Hermes bytecode
  • @fantom_disable_coverage: used to disable coverage collection for the test.
    • Example: @fantom_disable_coverage
    • Does not require a value.
  • @fantom_react_fb_flags: used to set overrides for internal React flags set
    in ReactNativeInternalFeatureFlags (Meta use only)

Setting @fantom_mode is mutually exclusive with setting any of
@fantom_native_opt, @fantom_js_opt, and @fantom_js_bytecode.

For all pragmas, except non-boolean feature flags, you can use a wildcard (*)
as value to run the test with all possible values for that pragma. For example,
this test:

javascript
/**
 * @fantom_flags jsOnlyTestFlag:*
 * @fantom_mode *
 */

Would be executed with these combinations of options:

jsOnlyTestFlag mode
false dev
true dev
false opt
true opt

With an output such as:

text
Test (jsOnlyTestFlag 🛑)
    [...]
  Test (jsOnlyTestFlag ✅)
    [...]
  Test (mode 🐛🔢, jsOnlyTestFlag 🛑)
    [...]
  Test (mode 🐛🔢, jsOnlyTestFlag ✅)
    [...]
  Test (mode 🚀, jsOnlyTestFlag 🛑)
    [...]
  Test (mode 🚀, jsOnlyTestFlag ✅)
    [...]

Debugging

[!WARNING] Meta-only: debugging only works on Meta's internal infrastructure
at the moment.

You can use environment variables to enable debugging for your tests. These
options can be combined to debug JS and C++ at the same time.

Debugging JS

To debug JavaScript, run your fantom test with the flag FANTOM_DEBUG_JS:

shell
FANTOM_DEBUG_JS=1 yarn fantom <regexForTestFiles>

This would open React Native DevTools, which would stop at a breakpoint at the
beginning of your test so you can debug it.

Debugging C++

To debug C++, run your fantom test with the flag FANTOM_DEBUG_CPP:

shell
FANTOM_DEBUG_CPP=1 yarn fantom <regexForTestFiles>

This would start a debugging session in VS Code with an initial breakpoint in
the Fantom CLI binary.

Address and thread sanitizer for C++

It's also possible to run the C++ side with the thread or address sanitizer
enabled, which can help with debugging memory and threading issues.

To enable the address sanitizer, run your fantom test with the flag
FANTOM_ENABLE_ASAN:

shell
FANTOM_ENABLE_ASAN=1 yarn fantom <regexForTestFiles>

For thread sanitizer, correspondingly, use flag FANTOM_ENABLE_TSAN:

shell
FANTOM_ENABLE_TSAN=1 yarn fantom <regexForTestFiles>

Profiling

JS sampling profiler

You can automatically record JS sampling profiler traces with the flag
FANTOM_PROFILE_JS:

shell
FANTOM_PROFILE_JS=1 yarn fantom <regexForTestFiles>

As part of the test results, you will see a message indicating where the traces
where saved, e.g.:

text
🔥 JS sampling profiler trace saved to /path/to/react-native/private/react-native-fantom/.out/js-traces/View-itest.js-2025-08-12T14:08:31.580Z.cpuprofile

If your test has multiple variants (when using wildcards in Fantom pragmas), a
trace will be created for each variant.

You can analyze the traces loading them in Chrome DevTools directly. You can
also open them in VS Code, which provides a built-in extension for analysis.

JS memory profiler

You can manually take JS memory heap snapshots during test execution using
Fantom.takeJSMemoryHeapSnapshot(). E.g.:

javascript
// Using the 3 snapshot method to detect memory leaks:

// Warm up
renderView();
destroyView();

// #1
Fantom.takeJSMemoryHeapSnapshot();

renderView();

// #2
Fantom.takeJSMemoryHeapSnapshot();

destroyView();

// #3
Fantom.takeJSMemoryHeapSnapshot();

// See the objects allocated between #1 and #2 that still exist in #3.

This function will force a garbage collection pass, take a snapshot of the JS
memory heap and print a message indicating where it was saved. E.g.:

text
💾 JS heap snapshot saved to /path/to/react-native/private/react-native-fantom/.out/js-heap-snapshots/View-itest.js-2025-08-12T14:29:25.987Z.heapsnapshot

You can have multiple calls to Fantom.takeJSMemoryHeapSnapshot() in your test,
and each one will create a different file.

C++ sampling profiler

You can automatically record C++ sampling profiler traces (with DWARF call
graphs) by wrapping the Fantom tester binary with Linux perf record. Run your
fantom test with the flag FANTOM_PROFILE_CPP:

shell
FANTOM_PROFILE_CPP=1 yarn fantom <regexForTestFiles>

Output is saved to .out/cpp-traces/perf-<timestamp>.data. Analyze it with:

shell
perf report -i .out/cpp-traces/perf-<timestamp>.data

This is not available on CI (the runner will throw an error if attempted) and
requires perf to be installed on the host.

Running the full suite locally

When running the entire suite locally, set FANTOM_FORCE_CI_MODE=1:

shell
FANTOM_FORCE_CI_MODE=1 yarn fantom

In CI mode, globalSetup pre-builds the entire
(hermesVariant × enableOptimized) binary matrix once up-front (see
global-setup/build.js) and per-test buck2
invocations are skipped. This eliminates buck2 daemon contention from the worker
pool, which can otherwise cause sporadic build failures when many workers race
to build different binary variants at the same time.

CI environments (SANDCASTLE, GITHUB_ACTIONS) auto-detect this mode — see
runner/EnvironmentOptions.js.

FAQ

How is this different from Jest tests?

Fantom runs C++ part of React Native, as well as JavaScript on Hermes VM -
unlike Jest tests that run on V8. This makes it possible to test things related
to shadow nodes, layout, events, scheduling, C++ state updates to name a few.
The results of Fabric are mounted in a mock UI tree that can be asserted against
and individual mounting instructions can be inspected.

You can even test your C++ code. For example, we have
Fantom tests for the new View Culling optimization,
which is written in C++.

Fantom exposes the method Fantom.scrollTo. This method will trigger an
onScroll event and configure the shadow tree to reflect the new content offset:

javascript
Fantom.scrollTo(scrollViewElement, {
  x: 0,
  y: 1,
});

expect(scrollViewElement.scrollTop).toBe(1);

How can I test logic that relies on timers (`setTimeout`/`setInterval`)?

Fantom runs on a deterministic virtual clock: wall-clock time never advances
on its own, and Fantom decides when the event loop and timers run. In the
default environment a zero-delay setTimeout(fn, 0) is dispatched on the next
work-loop tick, but timers with a positive delay (and any setInterval) stay
pending and never fire unless the virtual clock is advanced.

To advance the clock and run those callbacks, install a deterministic timer mock
with Fantom.installTimerMock(). While installed, you advance a virtual clock
to fire them, similar to jest.useFakeTimers():

javascript
const timers = Fantom.installTimerMock();

const callback = jest.fn();
setTimeout(callback, 100);

timers.advanceTimersByTime(50);
expect(callback).toHaveBeenCalledTimes(0);

timers.advanceTimersByTime(50);
expect(callback).toHaveBeenCalledTimes(1);

timers.uninstall();

advanceTimersByTime/runAllTimers run the work loop internally, so callbacks
have executed by the time they return. Use getPendingTimerCount() to inspect
how many timers are still scheduled, and uninstall() (typically in
afterEach) to restore the default behavior.

What can be tested with Fantom?

Fantom was designed to make it possible to test integration between React and
Fabric with the Jest API that many people are familiar with. You can write code
to simulate any kind of input into React and assert what the output is from
React Native core to the host platform. Fantom controls the app's message queue,
which gives complete control over scheduling. This makes it possible to write
tests that simulate scenarios where an event interrupts React rendering in a
deterministic fashion.

Even JavaScript only code can be tested with Fantom. We are considering fully
deprecating "vanilla" Jest in favor of Fantom for all JavaScript tests in React
Native.

Is Fantom ready for production use cases?

Fantom is a stable and reliable testing framework that is here to stay. If
you're planning to make changes to React or React Native internals, we highly
recommend using Fantom as your go-to testing solution.

Important Note: While Fantom is ideal for testing React and React Native
internals, it is not currently supported for testing application-specific code
in React Native apps. We'll keep you updated on any future developments that may
change this.

Where can I find examples of tests?

Look for files with the -itest.js suffix to find existing tests. The Fantom
test for its public API (Fantom-itest.js)
has simple examples you can learn from.

Are tests executed on Github CI?

Fantom tests are currently tied to Meta's infrastructure and do not run outside
of Meta's CI. We are working on migrating Fantom to Github CI. If you submit a
PR, the tests will run as part of the PR import process.

Can I gate individual tests within a suite to only run, or not run, with a specific feature flag?

Yes, you can use the @fantom_flags pragma to customize the flags that are
going to be used for the entire test suite, and conditionally define or exclude
the test in the suite depending on the flag value for that run. E.g.:

javascript
/**
 * @fantom_flags commonTestFlag:*
 */

import {ReactNativeFeatureFlags} from 'react-native/react-private-interface';

// The entire suite will be run with commonTestFlag set to true and false.
describe('MyTest', () => {
  it('should run with all values of the flag', () => {
    // ...
  });

  if (ReactNativeFeatureFlags.commonTestFlag()) {
    it('should only run when the flag is true', () => {
      // ...
    });
  }

  if (!ReactNativeFeatureFlags.commonTestFlag()) {
    it('should only run when the flag is false', () => {
      // ...
    });
  }
});

And the result would look like this:

text
PASS  MyTest-itest.js
  MyTest (commonTestFlag ❌)
    ✓ should run with all values of the flag
    ✓ should only run when the flag is false
  MyTest (commonTestFlag ✅)
    ✓ should run with all values of the flag
    ✓ should only run when the flag is true

If you have any questions not answered here, please reach out to us.

📐 Design

Fantom tests are meant to be written, executed and reported as regular Jests
tests. To accomplish that, Fantom is implemented as a Jest test runner.

Fantom provides a Jest configuration to use
its runner for any tests matching the -itest.js suffix
within the repository.

When Jest runs, for every file that matches that configuration, it calls into
the Fantom runner, which receives the path to the test file along with different
configuration objects.

The runner then follows these steps:

  1. It creates a single "executable" JavaScript file using Metro (with this
    configuration), containing the test itself, its
    dependencies and some glue code to run the test at runtime and report its
    results. Depending on the configured "mode" for the test, the runner might
    compile that JavaScript file into Hermes bytecode.
  2. It calls into the Fantom CLI passing the path to the executable JavaScript
    code (or Hermes bytecode) and the configured feature flags for the test. The
    Fantom CLI evaluates that code in the context of a React Native C++
    application and prints the results as JSON via its standard output.
  3. The runner receives the output from the Fantom CLI and reports the provided
    results back to Jest in the correct format.

🔗 Relationship with other systems

Part of this

  • The Fantom runner, which provides the integration point with Jest.
  • The Fantom CLI, which provides the execution environment for the Fantom
    JavaScript tests.
  • The Fantom benchmarking system, which is just a library and some automatic
    configuration on top of Fantom.

Used by this

  • Metro, to compile the test code into a single file consumable by the Fantom
    CLI.
  • Hermes Compiler, to compile the JavaScript test code into Hermes bytecode.
  • The
    React Native Feature Flags
    system, via the @fantom_flags pragmas defined in the docblock for test
    files.

Overview (`packages/react-native/Libraries/Renderer/README.md`)

React & React Native Versions

This page describes how React and React Native versions interact each other.
The version alignment between the two frameworks relies on two synchronization points:

  1. The versions in the package.json of the new app template. For example for React Native 0.68.1 the versions are aligned as follows:
text
"dependencies": {
    "react": "17.0.2",
    "react-native": "0.68.1"
  },
  1. The React renderers shipped with React Native inside this folder, the ./Libraries/Renderer folder, of React Native.

This practically means that you can't bump the version of React in your package.json to a later version,
as you will still be using the older renderer from the folder mentioned above. Bumping the react version in your package.json will lead to unexpected behaviors.

For the sake of React 18, the first version of React Native compatible with React 18 is 0.69.0. Users on React Native 0.68.0 and previous versions won't be able to use React 18.

If you use the React Native Upgrade Helper, you'll bump to the correct React version once you upgrade React Native.


Overview (`scripts/releases/ios-prebuild/__docs__/README.md`)

iOS Prebuild Scripts Documentation

This folder contains scripts for creating precompiled XCFrameworks for React
Native's dependencies. These scripts automate the process of downloading,
building, and packaging third-party libraries into distributable XCFramework
bundles for iOS.

Overview

The iOS prebuild system creates precompiled frameworks to reduce build times for
React Native iOS apps. Instead of compiling dependencies from source during
every build, these scripts package them as ready-to-use XCFrameworks.

The prebuild process creates a Swift package that builds frameworks for the
following 3rd party libraries:

  • boost
  • folly
  • glog
  • fmt
  • double-conversion
  • socketrocket
  • fast-float

Main Scripts

`cli.js`

Command-line interface for the prebuild system.

bash
node scripts/releases/prepare-ios-prebuilds.js

If no options are passed, the script executes all the steps in this order:

  • setup dependencies and prepares them
  • creates Swift package file
  • builds for all platforms and configurations
  • creates xcframeworks

Options:

Option Short Description
--setup -s Download and setup dependencies
--build -b Build dependencies for target platforms
--compose -c Compose XCFrameworks from built artifacts
--swiftpackage -w Generate Package.swift file
--platforms -p Target platforms (ios, macos, catalyst, tvos, visionos)
--configurations -g Build configurations (Debug, Release)
--dependencies -d Specific dependencies to process
--clean — Clean build folder before building
--identity -i Signing identity for frameworks

Integrating in your project with Cocoapods

To use the prebuilt React Native Dependencies XCFrameworks in your iOS project,
run pod install with the environment variable RCT_USE_RN_DEP set to 1:

bash
RCT_USE_RN_DEP=1 bundle exec pod install

This can be combined with RCT_USE_RN_DEP=1 to use both React Native and its
dependencies as prebuilt frameworks.

For debugging and troubleshooting the Cocoapods scripts, you can use the
following environment variables:

  • RCT_USE_RN_DEP: If set to 1, it will use the release tarball from Maven
    instead of building from source.
  • RCT_USE_LOCAL_RN_DEP: TEST ONLY If set, it will use a local tarball of
    ReactNativeDependencies if it exists.
  • RCT_DEPS_VERSION: TEST ONLY If set, it will override the version of
    ReactNativeDependencies to be used.

Overview (`packages/dev-middleware/src/inspector-proxy/__docs__/README.md`)

Inspector Proxy Protocol

🏠 Home

The inspector-proxy protocol facilitates Chrome DevTools Protocol (CDP) target
discovery and communication between debuggers (e.g., Chrome DevTools, VS
Code) and devices (processes containing React Native hosts). The proxy
multiplexes connections over a single WebSocket per device, allowing multiple
debuggers to connect to multiple pages on the same device.

🚀 Usage

Target Discovery (HTTP)

We implement a subset of the
Chrome DevTools Protocol's
HTTP endpoints
to allow debuggers to discover targets.

Endpoint Description
GET /json or /json/list List of debuggable pages
GET /json/version Protocol version info

Device Registration (WebSocket)

Devices register themselves with the proxy by connecting to /inspector/device:

text
ws://{host}/inspector/device?device={id}&name={name}&app={bundle_id}&profiling={true|false}
Parameter Required Description
device No* Logical device identifier. Auto-generated if omitted.
name No Human-readable device name. Defaults to "Unknown".
app No App bundle identifier. Defaults to "Unknown".
profiling No "true" if this is a profiling build. (Used for logging only)

*Recommended for connection persistence across app restarts.

Requirements for the `device` parameter

The intent of the logical device ID is to help with target discovery and
especially rediscovery - to reduce the number of times users need to
explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:

  1. SHOULD be stable for the current combination of physical device (or emulator
    instance) and app.
  2. SHOULD be stable across installs/launches of the same app on the same device
    (or emulator instance), though it MAY be user-resettable (so as to not
    require any special privacy permissions).
  3. MUST be unique across different apps on the same physical device (or
    emulator).
  4. MUST be unique across physical devices (or emulators).
  5. MUST be unique for each concurrent instance of the same app on the same
    physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability
requirements (SHOULD). In particular, on platforms that allow multiple instances
of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in
order to meet requirement 5. This is relevant, for example, on desktop
platforms.

Debugger Connection (WebSocket)

Debuggers connect to /inspector/debug to form a CDP session with a page:

text
ws://{host}/inspector/debug?device={device_id}&page={page_id}

Both device and page query parameters are required.

📐 Design

Architecture

text
┌─────────────────┐     ┌─────────────────────────┐     ┌────────────────┐
│    Debugger     │────▶│    Inspector Proxy      │◀────│     Device     │
│ (Chrome/VSCode) │     │      (Node.js)          │     │ (iOS/Android)  │
└─────────────────┘     └─────────────────────────┘     └────────────────┘
   WebSocket               HTTP + WebSocket               WebSocket
   /inspector/debug        /json, /json/list              /inspector/device
                           /json/version

Device ↔ Proxy Protocol

All messages are JSON-encoded WebSocket text frames:

typescript
interface Message {
  event: string;
  payload?: /* depends on event */;
}

Proxy → Device Messages

Event Payload Description
getPages (none) Request current page list. Sent periodically.
connect { pageId: string, sessionId: string } Prepare for debugger connection to page.
disconnect { pageId: string, sessionId: string } Terminate debugger session for page.
wrappedEvent { pageId: string, sessionId: string, wrappedEvent: string } Forward CDP message (JSON string) to page.

Device → Proxy Messages

Event Payload Description
getPages Page[] Current list of inspectable pages.
disconnect { pageId: string, sessionId?: string } Notify that page disconnected or rejected connection.
wrappedEvent { pageId: string, sessionId?: string, wrappedEvent: string } Forward CDP message (JSON string) from page.

Page Object

typescript
interface Page {
  id: string; // Unique page identifier (typically numeric string)
  title: string; // Display title
  app: string; // App bundle identifier
  description?: string; // Additional description
  capabilities?: {
    nativePageReloads?: boolean; // Target keeps the socket open across reloads
    nativeSourceCodeFetching?: boolean; // Target supports Network.loadNetworkResource
    supportsMultipleDebuggers?: boolean; // Supports concurrent debugger sessions
  };
}

Note: The value of supportsMultipleDebuggers SHOULD be consistent across
all pages for a given device.

Connection Lifecycle

Device Registration:

text
Device                              Proxy
   │                                  │
   │──── WS Connect ─────────────────▶│
   │     /inspector/device?...        │
   │                                  │
   │◀──── getPages ───────────────────│  (periodically)
   │                                  │
   │───── getPages response ─────────▶│
   │      (page list)                 │

Debugger Session:

text
Debugger            Proxy                        Device
   │                  │                            │
   │── WS Connect ───▶│                            │
   │   ?device&page   │── connect ────────────────▶│
   │                  │   {pageId, sessionId}      │
   │                  │                            │
   │── CDP Request ──▶│── wrappedEvent ───────────▶│
   │                  │   {pageId, sessionId,      │
   │                  │    wrappedEvent}           │
   │                  │                            │
   │                  │◀── wrappedEvent ───────────│
   │◀── CDP Response ─│   {pageId, sessionId,      │
   │                  │    wrappedEvent}           │
   │                  │                            │
   │── WS Close ─────▶│── disconnect ─────────────▶│
   │                  │   {pageId, sessionId}      │

Connection Rejection:

If a device cannot accept a connect (e.g., page doesn't exist), it should send
a disconnect back to the proxy for that pageId. Devices also send a
disconnect for each session when a page is removed.

For targets with nativePageReloads, the proxy closes the debugger connection
with [PAGE_REMOVED] when it receives a disconnect for its session (or for
its page, if sessionId is omitted).

Connection Semantics

Multi-Debugger Support

Multiple debuggers can connect simultaneously to the same page when both the
proxy and device support session multiplexing:

  1. Session IDs: The proxy assigns a unique, non-empty sessionId to each
    debugger connection. All messages include this sessionId for routing. This
    SHOULD be a UUID or other suitably unique and ephemeral identifier.

  2. Capability Detection: Devices report supportsMultipleDebuggers: true in
    their page capabilities to indicate session support.

  3. Backwards Compatibility: Legacy devices ignore sessionId fields in
    incoming messages and don't include them in responses.

Connection Rules

  1. Session-Capable Device: Multiple debuggers can connect to the same page
    simultaneously. Each connection has an independent session.

  2. Legacy Device (no supportsMultipleDebuggers): New debugger connections
    to an already-connected page disconnect the existing debugger. The proxy MUST
    NOT allow multiple debuggers to connect to the same page.

  3. Device Reconnection: If a device reconnects with the same device ID
    while debugger connections to the same logical device are open in the proxy,
    the proxy may attempt to preserve active debugger sessions by forwarding them
    to the new device.

WebSocket Close Reasons

The proxy uses specific close reasons that DevTools frontends may recognize:

Reason Context
[PAGE_NOT_FOUND] Debugger connected to non-existent page
[PAGE_REMOVED] Device ended or rejected the session
[CONNECTION_LOST] Device disconnected
[RECREATING_DEVICE] Device is reconnecting
[NEW_DEBUGGER_OPENED] Another debugger took over this page
[UNREGISTERED_DEVICE] Device ID not found
[INCORRECT_URL] Missing device/page query parameters

PageDescription (HTTP Response)

The /json endpoint returns enriched page descriptions based on those reported
by the device.

typescript
interface PageDescription {
  // Used for target selection
  id: string; // "{deviceId}-{pageId}"

  // Used for display
  title: string;
  description: string;
  deviceName: string;

  // Used for target matching
  appId: string;

  // Used for debugger connection
  webSocketDebuggerUrl: string;

  // React Native-specific metadata
  reactNative: {
    logicalDeviceId: string; // Used for target matching
    capabilities: {
      nativePageReloads?: boolean; // Used for target filtering
    };
  };
}

🔗 Relationship with other systems

Part of this

  • Device.js - Per-device connection handler in the proxy
  • InspectorProxy.js - Main proxy HTTP/WebSocket server

Used by this

  • Chrome DevTools Protocol (CDP) - The wrapped messages are CDP messages
    exchanged between DevTools frontends and JavaScript runtimes.
  • WebSocket - Transport layer for device and debugger connections.

Uses this

  • InspectorPackagerConnection (C++) - Shared device-side protocol
    implementation in ReactCommon/jsinspector-modern/.
  • Platform layers - iOS (RCTInspectorDevServerHelper.mm), Android
    (DevServerHelper.kt), and ReactCxxPlatform (Inspector.cpp) provide
    WebSocket I/O and threading.
  • openDebuggerMiddleware - Uses /json to discover targets for the
    /open-debugger endpoint.
  • OpenDebuggerKeyboardHandler - Uses /json to display target selection in
    the CLI.

Legacy Features

The following features exist for backward compatibility with older React Native
targets that lack modern capabilities. New implementations should set
appropriate capability flags and may ignore this section.

Synthetic Reloadable Page (Page ID `-1`)

For targets without the nativePageReloads capability, the proxy exposes a
synthetic page with ID -1 titled "React Native Experimental (Improved Chrome
Reloads)". Debuggers connecting to this page are automatically redirected to the
most recent React Native page, surviving page reloads.

When a new React Native page appears while a debugger is connected to -1:

  1. Proxy sends disconnect for the old page, connect for the new page
  2. Proxy sends Runtime.enable and Debugger.enable CDP commands to the new
    page
  3. When Runtime.executionContextCreated is received, proxy sends
    Runtime.executionContextsCleared to debugger, then Debugger.resume to
    device

URL Rewriting

For targets without the nativeSourceCodeFetching capability, the proxy
rewrites URLs in CDP messages:

  • Debugger.scriptParsed (device → debugger): Device-relative URLs are
    rewritten to debugger-relative URLs
  • Debugger.setBreakpointByUrl (debugger → device): URLs are rewritten back
    to device-relative form
  • Debugger.getScriptSource: Intercepted and handled by proxy via HTTP fetch
  • Network.loadNetworkResource: Returns CDP error (code -32601) to force
    frontend fallback

Additionally, if a script URL matches ^[0-9a-z]+$ (alphanumeric ID), the proxy
prepends file:// to ensure Chrome downloads source maps.

Legacy Reload Notification

For targets without nativePageReloads, when a disconnect event is received
for a page, the proxy sends {method: 'reload'} to the connected debugger to
signal a page reload.


Overview (`packages/react-native/React/DevSupport/__docs__/README.md`)

Bundler Connection (Apple platforms)

🏠 Home

When a React Native app on an Apple platform (iOS, tvOS, etc.) is launched, the
native side calculates where to load the JavaScript from: the bundler (Metro)
running on the development machine, or a bundle embedded in the app. In a Debug
build, the app then stays connected to Metro, which enables reloading, Fast
Refresh, opening the Dev Menu from the terminal, and debugging with React Native
DevTools. In a Release build, the app runs the embedded bundle and does not
contact Metro (see Build modes).

The app talks to Metro through two kinds of channels:

  1. HTTP: one-shot requests to check whether Metro is running, download the
    bundle, and open React Native DevTools.
  2. WebSocket: long-lived connections for Metro's commands (/message), Fast
    Refresh (/hot), and React Native DevTools (/inspector/device).

The details are split into the documents below. File paths are relative to
packages/react-native/; paths in examples are relative to the repository root.

Document Covers
Bundle URL How the app chooses where its JavaScript comes from, how to point it at a specific Metro, how Metro is found, what the bundle request asks for, and where the current URL is kept.
Downloading and Running the Bundle Fetching the bundle from Metro or reading it from the app, the loading banner, and load errors.
Reloading and Switching Metro What a reload restarts, what triggers it, how the URL changes between loads, and Configure Bundler.
Commands from Metro (/message) The WebSocket for reload and Dev Menu commands and its message protocol.
Fast Refresh (/hot) How Fast Refresh connects to Metro.
React Native DevTools (/inspector/device) How the app registers with Metro for debugging.
JavaScript Dev Tools That Use Metro LogBox symbolication, open in editor, links, split bundles and images.
Build Modes What each build configuration and compile flag enables.

🚀 Usage

The app chooses where its JavaScript comes from by implementing bundleURL in
the delegate of its RCTReactNativeFactory. Typically, Debug builds ask
RCTBundleURLProvider for a Metro URL and Release builds use the embedded
main.jsbundle. Bundle URL describes this, the ways to point
the app at a specific Metro, and how the URL can change.

📐 Design

All bundle loading goes through RCTHost
(ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm). When the app
starts, or reloads, React Native:

  1. Connects to Metro for terminal commands and React Native DevTools.
  2. Downloads the bundle while showing "Loading from Metro…" with progress.
  3. Runs the bundle, which renders the app, and connects Fast Refresh.
text
App delegate ── bundleURL (provider block) ──► RCTHost ── RCTBundleManager.bundleURL
                                                  │
                                            RCTInstance._loadJSBundle
                                                  │
         1. moduleForName:"DevSettings" ◄─────────┤
            RCTDevSettings.initialize             │
              ├─ RCTPackagerConnection ──────── WS /message ───────────► Metro  (receives reload and devMenu commands)
              └─ RCTInspectorDevServerHelper ── WS /inspector/device ──► Metro  (connects React Native DevTools)
                                                  │
         2. RCTHost.loadBundleAtURL ◄─────────────┤
            └─ RCTJavaScriptLoader ──────────── GET /<root>.bundle ────► Metro  (downloads the bundle)
                                                  │
         3. _loadJSBundle onComplete ◄────────────┘
            ├─ _loadScriptFromSource                                            (runs the bundle)
            │  └─ ReactInstance::loadScript                                     (runs the app's JavaScript)
            └─ setupHMRClientWithBundleURL
               └─ HMRClient.setup ───────────── WS /hot ───────────────► Metro  (applies hot updates)

Each step is described in its own document:
Downloading and Running the Bundle,
Commands from Metro,
React Native DevTools and
Fast Refresh.

Endpoints

Endpoint Client Purpose
GET /status RCTBundleURLProvider Is Metro running
GET /<root>.bundle?… RCTJavaScriptLoader Bundle download
GET /<root>/<name>.<ext> RCTBundleURLProvider resource URLs Native resources
WS /message?role=ios RCTPackagerConnection Reload, Dev Menu, custom
WS /hot HMRClient.js Fast Refresh
WS /inspector/device?… RCTCxxInspectorPackagerConnection React Native DevTools
POST /open-debugger RCTInspectorDevServerHelper Open React Native DevTools
POST /symbolicate symbolicateStackTrace.js (used by LogBox) Sends a JavaScript stack trace ({stack, extraData}); Metro maps each frame from the bundle back to the original source file, line and column using the source map
POST /open-stack-frame openFileInEditor.js (used when a LogBox stack frame or code frame is tapped) Sends {file, lineNumber}; Metro opens that file at that line in the editor on the development machine
POST /open-url openURLInBrowser.js Sends {url}; Metro opens the URL in the browser on the development machine

🔗 Relationship with other systems

Part of

  • React Native developer tooling on Apple platforms.

Part of this

  • React/Base/RCTBundleURLProvider: finds Metro, builds bundle and resource
    URLs, stores the settings.
  • React/Base/RCTBundleManager: RCTBundleConfiguration and the current bundle
    URL shared with native modules.
  • React/Base/RCTJavaScriptLoader, RCTMultipartDataTask,
    RCTMultipartStreamReader: download and file loading.
  • React/Base/RCTReloadCommand: reload listeners and the reload notification.
  • React/Base/RCTDevSupportHttpHeaders: extra headers for Metro requests.
  • React/DevSupport/RCTPackagerConnection, RCTPackagerClient,
    Libraries/WebSocket/RCTReconnectingWebSocket: the /message connection.
  • React/DevSupport/RCTInspectorDevServerHelper: the DevTools connection and
    /open-debugger.
  • React/CoreModules/RCTDevSettings: owns the /message connection, starts
    Fast Refresh and the DevTools connection.
  • React/CoreModules/RCTDevMenu: Reload and Configure Bundler.
  • React/CoreModules/RCTDevLoadingView: the loading banner.
  • Libraries/Utilities/HMRClient.js, Libraries/Core/Devtools/getDevServer.js:
    the JavaScript side.
  • scripts/react-native-xcode.sh: the embedded bundle and ip.txt.

Used by this

  • Metro and @react-native/dev-middleware: serve the endpoints above. See the
    Inspector proxy protocol.
  • jsinspector-modern: the DevTools socket protocol and flags (InspectorFlags
    adds the DevTools query parameters).
  • SocketRocket: WebSocket client.
  • metro-runtime HMRClient: the /hot protocol.

Uses this

  • RCTHost / RCTInstance: get the bundle URL, load the bundle, reload.
  • RCTReactNativeFactory / RCTRootViewFactory: pass the app's bundle URL
    provider and RCTBundleConfiguration to RCTHost.
  • JavaScript dev tools: LogBox symbolication, open in editor, image loading.

Overview (`packages/react-native/ReactApple/Libraries/RCTFoundation/README.md`)

RCTFoundation

RCTFoundation is a collection of lightweight utility libraries.

Rules for RCTFoundation libraries:

  • They must only depend on other RCTFoundation libraries.
  • Headers cannot contain C++.
  • They have modular set to true in BUCK.
  • They have complete_nullability set to true.
  • They have enabled Clang compiler warnings.
  • They have documentation.
  • They have unit tests.

Overview (`packages/react-native/ReactCommon/react/runtime/README.md`)

Bridgeless Mode for iOS

This library is not ready for integration for production nor local experimentation. Expect breaking changes regularly if you use any of these APIs. Use at your own risk!


Overview (`packages/react-native/scripts/featureflags/__docs__/README.md`)

Feature Flags

🏠 Home

This directory contains the flag definitions and codegen for the internal React
Native feature flags system.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/scripts/ios-prebuild/__docs__/README.md`)

iOS Prebuild Scripts

This directory contains scripts for prebuilding React Native itself into
XCFrameworks for iOS and related platforms.

Overview

These scripts automate the process of building React Native as a Swift Package
and packaging it into XCFrameworks that can be distributed and consumed by iOS
applications. The build process creates optimized frameworks for multiple
architectures and platforms.

Purpose

The prebuild scripts are used to:

  • Build React Native itself (not its dependencies) as XCFrameworks
  • Create distributable binaries for iOS, iOS Simulator, Catalyst.
  • Support both Debug and Release build configurations
  • Generate Debug Symbol (dSYM) files for debugging

Usage

Run the prebuild script from the command line:

bash
cd packages/react-native
node scripts/ios-prebuild

If no options are passed, the script executes all the steps in this order:

  • setup the codebase for all platforms and flavors
  • build for all platforms and flavors
  • compose xcframeworks
  • sign (if an identity is passed)

Options

Option Alias Type Default Description
--setup -s boolean - Download and setup dependencies
--build -b boolean - Build dependencies/platforms
--compose -c boolean - Compose XCFramework from built dependencies
--platforms -p array ['ios', 'ios-simulator', 'mac-catalyst'] Specify one or more platforms to build for
--flavor -f string Debug Specify the flavor to build: Debug or Release
--identity -i string - Specify the code signing identity to use for signing the frameworks
--help - boolean - Show help information

Output Structure

The build produces:

  • XCFrameworks in the specified output directory
  • Debug symbols (dSYM files) for debugging
  • Build products organized by platform and configuration

Architecture

The build system consists of several components:

`cli.js`

The main entry point that orchestrates the build process. It:

  • Parses command-line arguments
  • Validates build parameters
  • Coordinates the build, archiving, and XCFramework creation steps

`build.js`

Handles the Swift Package build process. It:

  • Executes xcodebuild commands with appropriate flags
  • Builds for specific platforms and build types (Debug/Release)
  • Locates and validates the generated framework artifacts
  • Uses build settings like BUILD_LIBRARY_FOR_DISTRIBUTION=YES for binary
    compatibility

`types.js`

Defines TypeScript/Flow type definitions for:

  • BuildFlavor: Debug or Release configurations
  • Destination: Target platforms (iOS, iOS Simulator, Catalyst, Vision,
    visionOS)
  • ArchiveOptions: Configuration options for the build process

`utils.js`

Provides utility functions including:

  • Logging functionality with prefixed output
  • Common helper functions used across scripts

Build Flags

The build process uses specific xcodebuild flags:

  • BUILD_LIBRARY_FOR_DISTRIBUTION=YES: Enables module stability
  • SKIP_INSTALL=NO: Ensures frameworks are properly installed
  • DEBUG_INFORMATION_FORMAT="dwarf-with-dsym": Generates debug symbols
  • OTHER_SWIFT_FLAGS="-no-verify-emitted-module-interface": Skips interface
    verification (useful for React Native modules due to the header structure not
    beeing modular)

Notes

  • These scripts build React Native itself, not third-party dependencies
  • The build process requires significant disk space for derived data
  • Build times vary depending on the target platform and configuration
  • XCFrameworks support multiple architectures in a single bundle

Header Resolution (headers-spec layout)

The prebuilt XCFrameworks ship a headers-spec layout so that header imports
resolve through plain header/framework search paths — there is no clang VFS
overlay
. The layout contract is defined and validated in code:

  • headers-spec.js: the executable layout contract (rules R1–R8) — which
    namespaces are hoisted, which carry module maps, and how collisions are
    rejected.
  • headers-inventory.js: scans the source tree to build the live header
    inventory that feeds the spec.
  • headers-compose.js: emits the layout. emitReactFrameworkHeaders() writes
    the React/ and bare-aliased headers into every slice's
    React.framework/Headers, and buildReactNativeHeadersXcframework()
    assembles the headers-only ReactNativeHeaders.xcframework carrying every
    other React Native namespace (incl. react/). The third-party dependency
    namespaces (folly, glog, boost, fmt, double-conversion,
    fast_float, SocketRocket) ship in the separate headers-only
    ReactNativeDependenciesHeaders.xcframework sidecar, built by the
    dependencies prebuild. The Hermes public headers (<hermes/...>) are folded
    into the published ReactNativeHeaders when the hermes-ios headers are
    staged; without them the compose ships no hermes/, unless --require-hermes
    is set, which makes it fail closed.

Artifacts

The prebuild (xcframework.js) always produces:

  • React.xcframework — the compiled React core. Each slice's React.framework
    carries the headers-spec layout (every <React/...> header + the framework
    module map). CocoaPods consumes that layout directly, through
    FRAMEWORK_SEARCH_PATHS. SwiftPM consumes the same headers indirectly,
    because the XCFramework is not a member of the Swift package graph. The
    consumer side stages a copy of React.framework/Headers into
    ReactHeadersTarget/include/React, rewrites framework module React to a
    plain module React, and vends it as the ReactHeaders target (see
    spm-header-paths-contract.md).
  • ReactNativeHeaders.xcframework — headers-only; carries every other React
    Native namespace. Consumed by SwiftPM as a binaryTarget and by CocoaPods via
    the React-Core-prebuilt pod (headers flattened onto the header search path).

CocoaPods consumption

The React-Core-prebuilt pod vends React.xcframework (so <React/...> and
@import React; resolve through the framework module via
FRAMEWORK_SEARCH_PATHS) and flattens ReactNativeHeaders.xcframework's
headers into a top-level Headers/ exposed on the pod header search path (so
<react/...>, <yoga/...>, <folly/...> resolve). rncore.rb adds the
HEADER_SEARCH_PATHS entry to React-Core-prebuilt/Headers for podspec,
aggregate (main app), and third-party pod targets. No -ivfsoverlay flags are
added.

Integrating in your project with Cocoapods

For consuming, debugging or troubleshooting when using Cocoapods scripts, you
can use the following environment variables:

  • RCT_USE_PREBUILT_RNCORE: If set to 1, it will use the release tarball from
    Maven instead of building from source.
  • RCT_TESTONLY_RNCORE_TARBALL_PATH: TEST ONLY If set, it will use a local
    tarball of RNCore if it exists.
  • RCT_TESTONLY_RNCORE_VERSION: TEST ONLY If set, it will override the
    version of RNCore to be used.
  • RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS: If set to 1, it will download the dSYMs
    for the prebuilt RNCore frameworks and install these in the framework folders

Overview (`packages/react-native/scripts/spm/__docs__/README.md`)

SwiftPM (Apple platforms) — Preview

🏠 Home

Preview. SwiftPM support is an early preview. The commands, flags,
generated layout, and distribution model may change in future releases. It is
not yet recommended for production. CocoaPods remains the supported default.

The scripts in scripts/spm/ let a React Native iOS app consume React Native
through Swift Package Manager instead of CocoaPods, using prebuilt
XCFrameworks. Support is opt-in and additive: npx react-native spm injects
package references into the app's existing .xcodeproj in place, and deinit
reverses exactly what it injected.

RFC0994
holds the motivation, the staged migration plan, and the open questions. The
documents here describe how the implementation works.

🚀 Usage

bash
cd ios
npx react-native spm      # add on first run, update thereafter

If an autolinked dependency ships no Package.swift, setup stops with
error: Package.swift is missing for library "<name>" and exit code 2.
Run
npx react-native spm scaffold, persist the manifest with patch-package, and
re-run setup. See
Community packages without a Package.swift.

See spm-scripts.md for the CLI actions and flags,
CocoaPods migration, brownfield apps, what to commit, fresh clones and CI, and
troubleshooting.

📐 Design

Three documents cover the design, each owning one area:

Document Covers
spm-scripts.md The tool itself: CLI surface, the six-step pipeline, every file it creates or modifies, the two auto-sync hooks, and how Debug/Release flavor selection works.
spm-header-paths-contract.md How headers and package references resolve. The contract is zero--I: no generated manifest carries a search path into React Native's own headers. Also covers remote mode.
spm-autolinking-plugins.md The extension seam for frameworks with their own module system (Expo is the first consumer): discovery, the full context/return contract including flavoredFrameworks, watchPaths and scriptPhases, and failure behavior.

Two ideas explain most of the architecture:

  • Headers go through SwiftPM; runtime binaries do not. A binaryTarget
    cannot vary by build configuration, but React Native ships flavored binaries.
    So the package graph vends headers only, and generated Xcode build settings
    link and embed the flavored frameworks. See
    Debug/Release flavor is automatic.
  • Generated state is regenerable, and the injection is reversible.
    Everything under build/ is gitignored and rebuilt from the app's
    package.json. Every edit to the .xcodeproj is recorded in a
    .spm-injected.json marker, so deinit can undo exactly that.

🔗 Relationship with other systems

Part of

Used by this

  • Prebuilt XCFrameworks from scripts/ios-prebuild/ —
    produces the React, ReactNativeDependencies, hermes-engine, and
    headers-only artifacts that these scripts download, stage, and link.
  • Codegen (generate-codegen-artifacts.js) — its output is installed as a
    local React-GeneratedCode package rather than a Pod.
  • @react-native-community/cli config — supplies the autolinking metadata
    (autolinking.json) that the SwiftPM autolinker turns into a Package.swift.
    Override it with the RCT_SPM_AUTOLINKING_CONFIG_COMMAND environment
    variable, or with --config-command when you run setup-apple-spm.js
    directly.

Uses this

  • Apps opting into SwiftPM, via the spm React Native CLI command.
  • Frameworks layering their own module system on top of React Native, via
    autolinking plugins.

Overview (`packages/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation/README.md`)

RCTDeprecation contains C macros to identify deprecated APIs at build-time.


Overview (`packages/react-native/ReactCommon/react/featureflags/__docs__/README.md`)

Feature Flags

🏠 Home

This directory contains the shared C++ implementation of the internal React
Native feature flags system.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/README.md`)

Options for DevTools Settings

Module Survives native restarts Survives JavaScript VM restarts
DevToolsRuntimeSettings No Yes
DevToolsSettings Yes Yes

Overview (`packages/react-native/ReactCommon/react/timing/__docs__/README.md`)

React Native Timing

🏠 Home

This directory contains the shared C++ implementation of high-resolution timing
primitives for React Native. These primitives provide precise time measurement
capabilities that align with Web standards while being optimized for the React
Native environment.

🚀 Usage

The timing primitives in this module are primarily used by internal React Native
systems that require precise time measurements. The monotonic clock is used for
these primitives: they should be used for measuring time intervals. These
primitives should not be used for wall times. They are not expected to be used
directly by application developers but serve as foundational components for
various React Native features.

Key primitives include:

  • HighResTimeStamp: A class representing a specific point in time with high
    precision.
  • HighResDuration: A class representing a duration of time with high
    precision.

These primitives support various operations:

cpp
// Getting the current high-resolution timestamp
auto start = HighResTimeStamp::now();

// Creating durations
auto duration = HighResDuration::fromNanoseconds(100);
auto durationMs = HighResDuration::fromMilliseconds(100);

// Arithmetic operations
auto later = start + duration;
auto elapsed = later - start;

// Converting to absolute time units of highest precision
auto end = HighResTimeStamp::now();
int64_t nanoseconds = (end - start).toNanoseconds();

// Converting to DOMHighResTimeStamp (for JavaScript interoperability)
double jsTimeValue = now.toDOMHighResTimeStamp();

📐 Design

The timing primitives are designed to align with Web standards while leveraging
C++'s type system and the performance characteristics of native code. The
implementation uses std::chrono internally but provides a more specialized
interface tailored to React Native's needs.

Platform-Specific Clock Sources

On different platforms, HighResTimeStamp uses different underlying clock
sources to ensure compatibility with platform-specific timing APIs:

  • iOS/macOS: Uses mach_absolute_time(), which measures time since device
    boot and excludes sleep time. This ensures compatibility with iOS system
    APIs (like UITouch.timestamp and NSProcessInfo.processInfo.systemUptime)
    and native performance logging systems that use the same clock source.

  • Other platforms: Uses std::chrono::steady_clock, which provides a
    monotonic clock that may include or exclude sleep time depending on the
    platform.

This design ensures that timestamps recorded in JavaScript and passed to native
systems will have correct timing on all platforms.

HighResTimeStamp

This class represents a specific point in time with high precision. It
encapsulates a std::chrono::steady_clock::time_point and provides methods to:

  • Convert to DOMHighResTimeStamp for JavaScript interoperability.
  • Perform arithmetic operations with durations.
  • Compare with other timestamps.

HighResDuration

This class represents a duration of time with high precision. It encapsulates a
std::chrono::duration and provides methods to:

  • Convert to DOMHighResTimeStamp for JavaScript interoperability.
  • Convert to an absolute number of nanoseconds.
  • Perform arithmetic operations.
  • Compare with other durations.

🔗 Relationship with other systems

Used by

  • Event Loop: Uses timing
    primitives for measuring task execution times and scheduling.
  • Web Performance API: Timing primitives are used to implement performance
    measurement APIs like PerformanceObserver entries (e.g., longtask and
    event).
  • React Native DevTools: The timing primitives integrate with the React Native
    DevTools tracing infrastructure to report the timing of tasks and events.
  • Web timing APIs: The timing primitives are designed to be compatible with Web
    timing concepts, making it easier to implement Web-compatible APIs in React
    Native.

Overview (`packages/react-native/src/private/featureflags/__docs__/README.md`)

Feature Flags

🏠 Home

Feature flags are values that determine the behavior of specific parts of React
Native. This directory contains the configuration for those values, and scripts
to generate files for different languages to access and customize them.

There are 2 types of feature flags:

  • Common: can be accessed from any language and they provide consistent values
    everywhere.
  • JS-only: they can only be accessed and customized from JavaScript.

🚀 Usage

Defining feature flags

The source of truth for the definition of the flags is the file
ReactNativeFeatureFlags.config.js in this directory.

Example contents:

javascript
module.exports = {
  common: {
    enableNativeBehavior: {
      description: 'Enable some behavior both in native and in JS.',
      defaultValue: false,
    },
  },
  jsOnly: {
    enableJSBehavior: {
      description: 'Enables some behavior in the JS layer.',
      defaultValue: false,
    },
  },
};

After any change to these definitions, the code that provides access to them
must be regenerated running this from the react-native repository:

shell
yarn featureflags --update

Accessing feature flags

You can access the common feature flags from anywhere in your application using
the ReactNativeFeatureFlags interface (available in C++/Objective-C++,
Kotlin/Java and JavaScript). JS-only feature flags can only be accessed from
JavaScript.

Accessing feature flags should be considered fast for all use cases. Feature
flags are cached at every layer, which prevents having to go through JNI when
accessing the values from Kotlin and through JSI when accessing the values from
JavaScript.

C++ / Objective-C

c++
#include <react/featureflags/ReactNativeFeatureFlags.h>

if (ReactNativeFeatureFlags::enableNativeBehavior()) {
  // do something
}

Kotlin

kotlin
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags

fun someMethod() {
  if (ReactNativeFeatureFlags.enableNativeBehavior()) {
    // do something
  }
}

JavaScript

javascript
import {ReactNativeFeatureFlags} from 'react-native/react-private-interface';

if (ReactNativeFeatureFlags.enableNativeBehavior()) {
  // Native flag
}

if (ReactNativeFeatureFlags.enableJSBehavior()) {
  // JS-only flag
}

Setting feature flag overrides

Feature flags provide the default values defined in the configuration unless
overrides are applied at the application level. Overrides for common feature
flags can only be defined in native, while overrides for JS-ony flags can only
be defined in JavaScript.

Overrides must be applied before any of the available feature flags has been
accessed. This prevents having inconsistent behavior during the lifecycle of the
application.

C++/Objective-C

c++
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/featureflags/ReactNativeFeatureFlagsDefaults.h>

class CustomReactNativeFeatureFlags : public ReactNativeFeatureFlagsDefaults {
 public:
  CustomReactNativeFeatureFlags();

  bool enableNativeBehavior() override {
    return true;
  }
}

ReactNativeFeatureFlags::override(std::make_unique<CustomReactNativeFeatureFlags>());

Kotlin

kotlin
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsDefaults

fun overrideFeatureFlags() {
  ReactNativeFeatureFlags.override(object : ReactNativeFeatureFlagsDefaults() {
    override fun useMicrotasks(): Boolean = true
  })
}

JavaScript

javascript
import {ReactNativeFeatureFlags} from 'react-native/react-private-interface';

ReactNativeFeatureFlags.override({
  enableJSBehavior: () => true,
});

Reviewing feature flags

You can find the list of feature flags with their configuration in
ReactNativeFeatureFlags.config.js,
but you can also use the CLI to list them:

shell
yarn featureflags --print

Which would print something like:

text
/* Detailed source-code truncated for AI context efficiency. */

If you need to consume this as structured data, you can print it as JSON using:

shell
yarn featureflags --print --json

📐 Design

The architecture of this feature flags system can be described as follows:

  • A shared C++ core, where we provide access to the flags and allow
    customizations.
  • A Kotlin/Java interface that allows accessing and customizing the values in
    the C++ core (via JNI).
  • A JavaScript interface that allows accessing the common values (via a native
    module) and accessing and customizing the JS-only values.

Most of the code for this system is automatically generated from
ReactNativeFeatureFlags.config.js.
The entrypoint for the codegen can be found
here.

The codegen uses a simple templating system based on JavaScript template strings
to generate the files (see files ending with -template.js in the
templates directory),

🔗 Relationship with other systems

Part of this

Used by this

  • The only dependency is the C++ TurboModule infrastructure (including codegen),
    as the JavaScript API uses it to access the feature flag values from native.

Uses this

This system is used extensively throughout the codebase and it evolves over time
as feature flags are added or cleaned up.


Overview (`packages/react-native/src/private/specs_DEPRECATED/__docs__/README.md`)

specs_DEPRECATED

This directory was created to move all the existing native module and native
component specs from the legacy Libraries directory. This was necessary
because we needed to change the directory for the codegen from Libraries to
src/private, and specs in Libraries needed to continue working.

Do not create new specs in this directory. Instead, create them colocated
with the rest of the logic in an appropriate directory in src/private.

Over time, as we move code from Libraries to src/private, we should be
moving the specs in this directory out until it is empty and we can remove it.


Overview (`packages/rn-tester/js/examples/Experimental/PlatformTest/README.md`)

RNTester PlatformTest

A barebones manual testing framework designed to work as a mechanism for recreating Web Platform Tests in React Native in order to verify their compliance with W3C specifications.

Usage

Any new test case will start as a new React component which at the very least accepts a harness prop which will contain all of the framework's testing APIs. This component you write will be passed into the component prop of a RNTesterPlatformTest element whose other props are used for documenting your test. Rendering this RNTesterPlatformTest will look similar to something like this:

js
function ExampleTestCase ({ harness }) { /* ... */ }

<RNTesterPlatformTest
  title="Example Test"
  description="Imagine there's a detailed description of this example test here"
  instructions={[
    "This is the example test's first step",
    "A second step",
    "A third step",
  ]}
  component={ExampleTestCase}
/>

As of writing this README there are 2 different types of tests that the harness prop provides:

`test(testcase: (TestContext) => void, testName: string, options?: TestOptions)`

This is a method to create "regular" test reminiscent of other frameworks such as Jest. These are meant to be run imperatively, and while that means that they technically could work in a useEffect hook as a way to run the test "on mount" — it is instead recommended to try and keep these tests in callbacks instead. A good alternative to running the test on mount would be to instead put the test in a callback and render a "Start Test" button which executes the callback.

The first argument is the closure in which you will run your test and make assertions. The assertions are contained in the TestContext object which is provided in the test closure's first argument and contains the following assertions:

  • assert_true(a: boolean, description: string): void
  • assert_equals(a: any, b: any, description: string): void
  • assert_greater_than_equal(a: number, b: number, description: string): void
  • assert_less_than_equal(a: number, b: number, description: string): void

An optional third argument can be used for specifying additional options to the test — that object currently has the following properties (all of which are optional themselves):

  • skip: boolean: In cases where we want the test to be registered but we don't want it to contribute to the pass/fail count.

Here's what a basic/contrived example which verifies the layout of a basic view:

js
const EXPECTED_WIDTH = 100;
const EXPECTED_HEIGHT = 200;

function BasicLayoutTestCase({harness}) {
  const viewRef = useRef(null);

  const runTest = useCallback(() => {
    const view = viewRef.current;
    if (view != null) {
      view.measureInWindow(({width, height}) => {
        harness.test(({assert_equals}) => {
          assert_equals(
            width,
            EXPECTED_WIDTH,
            `view's computed width should be ${EXPECTED_WIDTH}`,
          );
          assert_equals(
            height,
            EXPECTED_HEIGHT,
            `view's computed width should be ${EXPECTED_HEIGHT}`,
          );
        }, "view's width and height are correct");
      });
    }
  }, [harness]);

  return (
    <>
      <View
        ref={viewRef}
        style={{width: EXPECTED_WIDTH, height: EXPECTED_HEIGHT}}
      />
      <Button title="Start Test" onPress={runTest} />
    </>
  );
}

`useAsyncTest(description: string, timeoutMs?: number): AsyncPlatformTest`

This is a hook which can be used to represent tests that expect something to happen some time in the future. If the test isn't marked as "done" within a certain amount of time (10 seconds by default but can be optionally specified in the hook's second argument). This hook returns an object containing a done function which is used for marking the completion of the async test.

Here's what a basic example would look like for verifying that pointermove events are emitted:

js
function BasicPointerMoveTestCase({harness}) {
  const testPointerMove = harness.useAsyncTest('pointermove event received');

  return (
    <View
      style={{width: 100, height: 100, backgroundColor: 'black'}}
      onPointerMove={() => testPointerMove.done()}
    />
  );
}

Overview (`packages/react-native/ReactCommon/react/nativemodule/featureflags/__docs__/README.md`)

Feature Flags

🏠 Home

This directory contains the native C++ TurboModule for the internal React Native
feature flags system.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/__docs__/README.md`)

IntersectionObserver

🏠 Home

This directory contains the native module used by the
IntersectionObserver API
in React Native.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/nativemodule/microtasks/__docs__/README.md`)

Microtasks

🏠 Home

This directory contains the native module used to implement queueMicrotask in
React Native, which schedules microtasks in the
Event Loop.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/nativemodule/mutationobserver/__docs__/README.md`)

MutationObserver

🏠 Home

This directory contains the native module used by the
MutationObserver API
in React Native.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/nativemodule/resizeobserver/__docs__/README.md`)

ResizeObserver

🏠 Home

This directory contains the native module used by the
ResizeObserver API
in React Native.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/nativemodule/samples/ReactCommon/README.md`)

Refer to packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h for a concrete example.
See: https://reactnative.dev/docs/the-new-architecture/pure-cxx-modules


Overview (`packages/react-native/ReactCommon/react/renderer/runtimescheduler/__docs__/README.md`)

Event Loop

🏠 Home

The Event Loop is the formalization of the execution model for JavaScript in
React Native, and how that model synchronizes with rendering work in the host
platform.

Its main goals are:

  • To make the behavior of the framework more predictable, to help developers
    build more reliable and performant apps.
  • To increase the alignment with Web specifications, to simplify the adoption of
    Web APIs in React Native.
  • (Secondary) Provide a solid foundation for general performance optimizations
    relying on better scheduling or sequencing.

The implementation of the event loop in React Native is aligned with
its definition in the HTML specification.
React Native only implements a subset of it, in order to support its existing
semantics and APIs.

[!NOTE]

The Event Loop in React Native was originally proposed in this
RFC,
which contains additional context (e.g.: how this was introduced and what
React Native had before).

🚀 Usage

The event loop is an implementation detail so it is not used directly, but
several APIs integrate deeply with it:

  • State updates processed by React are scheduled to be flushed (rendered in the
    host platform) at the end of the current event loop tick.
  • Promises, queueMicrotask and APIs like MutationObserver add microtasks to
    the microtask queue, processed as part of the current event loop tick.
  • Timers and APIs like requestIdleCallback schedule tasks to be processed by
    the event loop. In the case of requestIdleCallback, the tasks are scheduled
    with specific priorities.
  • PerformanceObserver entries like longtask and event provide timing
    information about different parts of the event loop.

One of the most important semantics of the event loop is the atomicity of UI
updates in tasks
. All changes to the UI triggered from JavaScript in a task
(processing state updates in React, dispatching view commands, etc.) are always
flushed together to the host platform, so the UI is never updated with partial
work done within a task. For example:

  1. We dispatch an event to JavaScript and execute one or more event handlers.
  2. Those event handlers do one or more state updates in React. React schedules a
    microtask to process them.
  3. In a microtask, React processes all those state updates together,
    re-rendering the necessary components and committing at the end.
  4. As part of the commit, we might execute layout effects that might trigger
    more state updates. Those state updates are processed synchronously at the
    end of the commit.
  5. At the end of the current microtask, we execute the rest of the microtasks.
  6. When the microtask queue is empty, we flush all changes to the host platform.

In code, that could look like this:

javascript
function App(props) {
  const [showContent, setShowContent] = useState(false);

  return (
    <>
      <Header
        onPressButton={event => {
          // A task executes all the event handlers.
          // The state update is processed in a microtask.
          setShowContent(true);
        }}
      />
      {showContent ? <Content /> : null}
    </>
  );
}

function Content(props) {
  const [width, setWidth] = useState(0);

  // Executed synchronously as part of the commit phase, in the microtask.
  useLayoutEffect(() => {
    // This state update is processed synchronously in the same microtask.
    // UI updates are still not flushed to the host platform until the end
    // of the event loop tick, so the user never saw this changed widths.
    setWidth(ref.getBoundingClientRect().width);
  }, []);

  return (
    <>
      <SomeView ref={ref} />
      <OtherComponent width={width} />
    </>
  );
}

📐 Design

The conceptual model is aligned with
the model on the Web

(from which it borrows some concepts and steps), while still benefiting from
the React Native threading model
.

The event loop continuously goes through these steps (in what we refer to as an
"event loop tick"):

  1. Select the next task to execute among all tasks waiting for execution.
  2. Execute the selected task.
  3. Execute all scheduled microtasks.
  4. Update the rendering.

One of the key benefits of this model is that each event loop iteration
represents an atomic UI update
. This helps reason about whether work scheduled
within a specific task should be rendered together or not. If it is a step in
the same task, scheduled as a microtask or one of the specific sub-steps in
updating the rendering, then it will be atomic. Otherwise, if it was scheduled
as a separate task (e.g.: using timers, native callbacks, etc.) it would
constitute a separate UI update.

Detailed steps

1. Task selection

On the Web, task selection is an implementation detail left to browsers to
decide.

In React Native, we rely on RuntimeScheduler for task selection, which already
supports the execution of tasks with priorities. The criteria it uses is:

  1. If there are expired tasks, select expired tasks in the order in which they
    expired.
  2. Otherwise, select tasks by priority, in the order in which they were
    scheduled.

The Web specification defines the concept of task queues as a mechanism to
ensure that certain types of tasks execute in a specific order (e.g.: events).
React Native has the concept of event queue that serve the same purpose.

2. Task execution

Task execution is calling the JavaScript function or C++ callback that is
associated to the task that was scheduled.

3. Microtask execution

In this step we drain the microtask queue, executing all the microtasks in
order. A microtask can schedule additional microtasks, so incorrect product
logic could lead to an infinite loop. This is expected and it is the same
behavior as on the Web.

4. Update the rendering

The last step applies work that browsers perform after task and microtask
execution, before the next task is selected (see
HTML — update the rendering).

In RuntimeScheduler_Modern, this step
currently:

  1. Dispatches pending Event Timing entries (when enabled).
  2. Runs
    Resize Observer
    observations via
    RuntimeSchedulerResizeObserverDelegate::runResizeObservations(). See the
    ResizeObserver API.
  3. Updates Intersection Observer
    observations via RuntimeSchedulerIntersectionObserverDelegate.
  4. Drains pending rendering updates (commits, view commands) so the host
    platform can apply mutations.

Observer steps run before the rendering-update queue is drained. Resize and
intersection observer integration is only implemented in
RuntimeScheduler_Modern; RuntimeScheduler_Legacy treats the corresponding
delegates as no-ops.

Additional browser work in this phase (e.g. animation frame callbacks) may be
added in the future.

Synchronous execution, events and rendering

The formalization of the event loop also allows the efficient implementation of
synchronous events. Because the event loop defines the tick as an atomic unit of
work that leads to a full UI update, we can also use it as the unit of work to
be executed in the UI thread.

Implementation

Most of the implementation of the event loop is done in the
RuntimeScheduler class (specifically in
RuntimeScheduler_modern, which is the version
used in the new architecture).

That class implements both the task scheduler that handles priorities and the
processing of those tasks within the event loop.

🔗 Relationship with other systems

Part of this

  • The implementation of
    queueMicrotask to
    schedule microtasks in the event loop.

Used by this

  • React timing primitives, to do time measurements of the execution of certain
    parts of the event loop to integrate with PerformanceObserver.
  • Indirectly, it reports long tasks and event timing to PerformanceObserver.
  • It also integrates with the React Native DevTools tracing infrastructure to
    report the timing of tasks.

Uses this

  • MutationObserver
    uses the event loop to schedule mutation observer callbacks as microtasks.
  • ResizeObserver
    uses the event loop to run resize observations during the "update the
    rendering" step.
  • IntersectionObserver
    uses the event loop to update intersection observations during the same step.
  • Scheduler integrates with the event loop to register reporters (for
    PerformanceObserver) and schedules the specific work to be done as part of
    the rendering updates.

Overview (`packages/react-native/src/private/webapis/intersectionobserver/__docs__/README.md`)

IntersectionObserver

🏠 Home

This directory contains the React Native implementation of the
IntersectionObserver API.

🚀 Usage

IntersectionObserver is meant to be used from JavaScript, exposed as a global
class.

📐 Design

This is the high-level design of the IntersectionObserver API:

The global IntersectionObserver class is defined in JavaScript and it does its
setup using a native module.

In native, it uses mount hooks from UIManager to run intersection calculations
and update the state of the observers. When a mount operation is reported from
the host platform to UIManager, UIManager notifies the mount hook that
triggers a check on the observers that are observing intersections on that
surface. If the intersection triggers a state change, the notification is
dispatched to the JavaScript observers.

For the initial intersection notification (which is meant to report the state at
the time of observation), we run a step in the Event Loop as specified in the
Web spec (via RuntimeSchedulerIntersectionObserverDelegate), where we check if
there are pending transactions for the given surface. If there are, we just wait
for the transaction to be mounted and use the mount hook to report the initial
notification. If there aren't, we dispatch the notification immediately.

🔗 Relationship with other systems

Part of this

Used by this

  • This relies on mount hooks provided by UIManager, and on the information
    provided by the shadow tree registry provided by UIManager as well.
  • It uses the C++ TurboModule infra for communication between JavaScript and
    native.

Uses this

  • This is an API meant to be used by end users. It is not used directly but any
    other parts of the platform.

Overview (`packages/react-native/src/private/webapis/mutationobserver/__docs__/README.md`)

MutationObserver

🏠 Home

This directory contains the React Native implementation of the
MutationObserver API.

🚀 Usage

MutationObserver is meant to be used from JavaScript, exposed as a global
class.

📐 Design

This is the high-level design of the MutationObserver API:

The global MutationObserver class is defined in JavaScript and it does its
setup using a native module.

In native, it relies on ShadowTree commit hooks to get notifications about
changes in the tree, and detects mutations using the before/after revisions. It
relies on referential equality of the shadow nodes for performance
optimizations.

🔗 Relationship with other systems

Part of this

Used by this

  • This relies on ShadowTree commit hooks provided by UIManager.
  • It uses the C++ TurboModule infra for communication between JavaScript and
    native.
  • It uses the
    Event Loop
    to schedule mutation observer callbacks as microtasks.

Uses this

  • This is an API meant to be used by end users. It is not used directly but any
    other parts of the platform.

Overview (`packages/react-native/src/private/webapis/resizeobserver/__docs__/README.md`)

ResizeObserver

🏠 Home

This directory contains the React Native implementation of the
ResizeObserver API.

🚀 Usage

ResizeObserver is meant to be used from JavaScript, exposed as a global class.

📐 Design

This is the high-level design of the ResizeObserver API:

The global ResizeObserver class is defined in JavaScript and it does its setup
using a native module.

In native, it relies on commit hooks from UIManager to learn which nodes had
layout updates after each commit (shadowTreeDidCommit with the affected layout
nodes). The commit hook records which observed targets may have resized. A
dedicated step in the Event Loop (as specified in the Web spec, via
RuntimeSchedulerResizeObserverDelegate) then runs resize observations against
the latest committed tree and notifies JavaScript when there are pending
entries. JavaScript pulls those entries via takeRecords and dispatches them to
the right observers.

Unlike layout events (onLayout), ResizeObserver lets callers choose which
box to observe (content-box, border-box, or device-pixel-content-box) and
delivers sizes for those boxes in the notification. Notifications are delivered
from the «update the rendering» step of the Event Loop (via
RuntimeSchedulerResizeObserverDelegate) — after the task that committed the
layout change and that task's microtask checkpoint, not synchronously when
layout is computed.

🔗 Relationship with other systems

Part of this

Used by this

  • This relies on ShadowTree commit hooks provided by UIManager, including
    the list of nodes whose layout changed after each commit.
  • It uses the C++ TurboModule infra for communication between JavaScript and
    native.
  • It uses the
    Event Loop
    to run resize observations as a dedicated step.

Uses this

  • This is an API meant to be used by end users. It is not used directly by any
    other parts of the platform.

Overview (`packages/react-native/ReactAndroid/src/main/jni/first-party/jni-hack/README.md`)

This buck module exists so that targets that need to be built against both 1) Android (where we can and should use the Android NDK jni headers) and 2) the host platform(generally for local unit tests) can depend on a single target and get the right jni header for whatever platform they're building against automatically.


Overview (`packages/react-native/ReactCommon/react/renderer/observers/intersection/__docs__/README.md`)

IntersectionObserver

🏠 Home

This directory contains the C++ implementation of the
IntersectionObserver API
in React Native.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/renderer/observers/mutation/__docs__/README.md`)

MutationObserver

🏠 Home

This directory contains the C++ implementation of the
MutationObserver API
in React Native.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactCommon/react/renderer/observers/resize/__docs__/README.md`)

ResizeObserver

🏠 Home

This directory contains the C++ implementation of the
ResizeObserver API
in React Native.

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/src/private/testing/fantom/specs/__docs__/README.md`)

Fantom

🏠 Home

This directory contains the specifications for internal native modules used by
Fantom. These are not meant to be consumed directly.

[!NOTE]

These definitions are here (in the react-native package) and not in the
@react-native/fantom package just for convenience, to reuse the same codegen
configuration we already have (that is configured to look for definitions in
src/).

🔗 Relationship with other systems

Part of


Overview (`packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/README.md`)

Bridgeless Mode for Android

This library is not ready for integration for production nor local experimentation. Expect breaking changes regularly if you use any of these APIs. Use at your own risk!


Overview (`packages/react-native/ReactAndroid/src/main/java/com/facebook/react/activityresult/__docs__/README.md`)

ActivityResultContracts for native modules

🏠 Home

This package lets an Android native module register an AndroidX
ActivityResultContract
and receive results. The consumer app does not need to change its
MainActivity, add manifest entries, or ship extra Activities.

Before this, modules had to use ActivityEventListener with self-assigned int
request codes, which live in a global namespace with no coordination between
libraries. Calling registerForActivityResult on getCurrentActivity() does
not work either: AndroidX only allows it before the Activity is started, and
native modules are created lazily, long after that. See
facebook/react-native#33639
(Health Connect, whose permission contract cannot be used without
registerForActivityResult).

🚀 Usage

The API is ReactContext.registerForActivityResult. It has the same shape as
ComponentActivity.registerForActivityResult,
plus a leading owner argument that scopes the registration key. You can
register at any time. A field initializer is the recommended spot. The returned
launcher connects to the real registry once an Activity is available.

kotlin
class MyModule(private val context: ReactApplicationContext) :
    NativeMyModuleSpec(context) {

  private var pendingPromise: Promise? = null

  private val requestPermission =
      context.registerForActivityResult(
          /* owner = */ this,
          ActivityResultContracts.RequestPermission()) { isGranted ->
        pendingPromise?.resolve(isGranted)
        pendingPromise = null
      }

  override fun requestCameraPermission(promise: Promise) {
    pendingPromise = promise
    requestPermission.launch(Manifest.permission.CAMERA)
  }
}

Stock AndroidX contracts work unchanged, with their own input and output types
(for example PickVisualMedia).

Registration keys and collisions

Registrations are keyed by "<owner class>:<contract class>", so two unrelated
libraries can register the same stock contract without clashing. Pass a stable,
long-lived owner, normally the module itself. Only named classes are allowed
as owners: an anonymous object gets a generated class name that can change
between builds, which breaks result delivery after the process is killed and
restored, so passing one throws IllegalArgumentException at registration.

The same stability concern applies to minification. If the app minifies class
names (R8/ProGuard), the obfuscated name of the owner class is not guaranteed to
be the same from one build to the next, so a result delivered after an app
update can be dropped. Keep the owner class's name (for example with
-keepnames) if results must survive across builds.

Registering the same contract class twice from one owner throws
IllegalStateException. In that case use the overload that takes a key:

kotlin
private val pickAvatar = ctx.registerForActivityResult(this, "avatar", GetContent()) { }
private val pickBanner = ctx.registerForActivityResult(this, "banner", GetContent()) { }

The key is added to the owner-and-contract prefix, not used instead of it. It
only has to be unique among that owner's launchers of that contract, and it can
never clash with another library's keys. It must stay the same across process
restarts, so derive it from a constant.

Why not automatic keys, like ComponentActivity's counter? Modules are created
lazily, in whatever order JS touches them. After the process is killed and
restored, the same counter value could belong to a different module, and a
restored result would reach the wrong callback. Keys built from class names do
not depend on creation order.

Contract parameters that come from JS

Contract constructor arguments are fixed when you register. If a value comes
from JS on each call, put it in the contract's input type instead: subclass the
stock contract and pass the value through launch(). See PickUpToMedia in
SampleTurboModule.kt, which does this for the photo picker's item limit.

Working examples

  • SampleTurboModule.kt
    (ReactCommon/react/nativemodule/samples/platform/android/):
    requestSamplePermission, pickMedia, pickMultipleMedia, and
    startSecondActivity (multi-Activity regression check).
  • rn-tester screens: TurboModule/SampleTurboModuleExample.js and
    PhotoPickerAndroid/PhotoPickerAndroid.js.

📐 Design

ReactActivity extends ComponentActivity, so the host Activity already owns a
real ActivityResultRegistry. This package only bridges the timing gap between
lazily-created modules and that registry.

  • ReactActivityResultCaller / ReactActivityResultCallerImpl (internal):
    owned by the ReactContext. Holds the (key, contract, callback)
    registrations and connects them to the current Activity's registry, right away
    if an Activity exists, otherwise on the next onHostResume.
  • DeferredActivityResultLauncher (internal): the launcher handed to callers.
    It forwards to the real AndroidX launcher once connected. A launch() made
    before that is stored (latest wins) and fired on connect.
  • Registrations outlive any single Activity. onHostDestroy disconnects them
    but keeps them, and because the keys stay the same, AndroidX can deliver a
    result that arrives after the Activity was recreated.
  • Every onHostResume checks each launcher against the current registry, not
    just whether it is connected to something. With more than one Activity, the
    new Activity resumes before the old one is destroyed, and the old one's
    onHostDestroy never runs because currentActivity has already moved on. A
    launcher that only checked "am I connected?" would stay attached to the old
    Activity's registry: that Activity could never be freed, and launches from the
    new screen would go to the old one.

Threading

ActivityResultRegistry must only be used from the UI thread, but nothing
enforces that at runtime; calls from other threads corrupt its internal maps
silently. React Native calls in from the JS thread (registrations in field
initializers) and from the native-modules thread (launch()), so:

  • The bookkeeping used for collision detection is a concurrent map and can be
    used from any thread. Claiming a key is a single atomic step. Registration
    stays synchronous: you get the launcher back immediately, and a duplicate key
    throws from your own call.
  • Every call that reaches the registry (register, launch, unregister) is
    forwarded to the UI thread, and so is the launcher's connection state (checked
    with assertions in debug builds).

Notes for library authors:

  • Register early, in a field initializer or the constructor. Only launching
    needs an Activity.
  • An Activity that is not an ActivityResultRegistryOwner cannot serve
    launchers; binding to one throws IllegalStateException. In practice every
    ComponentActivity (including ReactActivity) is a registry owner.
  • After the process is killed and restored, AndroidX redelivers a pending result
    under the same key, but any state your module held for the call (typically a
    Promise) is gone. Write callbacks so they tolerate firing with no pending
    state.
  • unregister() on the returned launcher removes the registration and frees the
    key.

🔗 Relationship with other systems

Part of

Used by this

  • com.facebook.react.bridge.ReactContext: exposes the public
    registerForActivityResult methods, owns the caller instance, and drives
    connecting and disconnecting through its lifecycle events.
  • AndroidX androidx.activity.result: the contracts, launchers, and registry
    that actually start activities and deliver results.

Uses this

  • SampleTurboModule (demo) and, in the future, third-party modules that need
    activity results or AndroidX permission contracts (for example Health
    Connect).

This API coexists with ActivityEventListener: results claimed by the AndroidX
registry are consumed by it, and everything else still reaches
ActivityEventListener.onActivityResult. The listener remains the right tool
for intents a module builds and starts itself.


Overview (`packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/__docs__/README.md`)

Android event dispatching

🏠 Home

This directory contains Kotlin classes specific to Android event dispatching in
the new architecture.

📐 Design

🔗 Relationship with other systems

Part of

  • Android's Fabric implementation

Used by this

This component is tightly coupled with the legacy event dispatch mechanism. Some
of the interfaces and components live in the com.facebook.react.uimanager.events
package so that they may reference internal API's.


Overview (`packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/__docs__/README.md`)

Feature Flags

🏠 Home

This directory contains the Java/Kotlin bindings for the internal React Native
feature flags system.

🔗 Relationship with other systems

Part of


--- METRICS ---