react/react-native
github.com/react/react-native ↗ · 3998 files · 26.0 MB · scan capped
Learn once, write anywhere: Create native apps for Android, iOS, and more using React Getting Started · Learn the Basics · Showcase · Contribute · Community · Support React Native React Native lets you build native apps using React.
Overview
React Native is a Yarn-workspaces monorepo that produces the `react-native` npm package plus ~25 sibling packages (@react-native/codegen, dev-middleware, community-cli-plugin, metro-config, gradle-plugin, debugger-frontend, etc.). It implements a React renderer for iOS and Android: Flow-typed JavaScript in packages/react-native (legacy `Libraries/` being migrated to `src/private/`), a shared C++ core in ReactCommon (Fabric renderer, TurboModules, JSI, Hermes glue, Yoga layout, jsinspector-modern), and platform runtimes in ReactAndroid (Kotlin/Java) and React/ReactApple (Objective-C++/Swift). Its distinctive machinery is a codegen pipeline that generates type-safe native bindings from `Native*.js`/`*NativeComponent.js` specs, a feature-flag system generated across JS/Java/C++, and Fantom, a headless integration test runner that executes JS on Hermes against real C++ Fabric with a mock host platform.
System map
Yarn v1 workspaces monorepo rooted at package.json (@react-native/monorepo). The flagship artifact is packages/react-native, which ships the JS public API (index.js lazy getters), the C++ core (ReactCommon), the Android runtime (ReactAndroid, Gradle), and the Apple runtime (React + ReactApple, CocoaPods podspecs + SwiftPM Package.swift). Tooling packages around it provide the dev server CLI (@react-native/community-cli-plugin), CDP inspector proxy (@react-native/dev-middleware), Metro defaults (@react-native/metro-config), codegen (@react-native/codegen), and the Gradle plugin. Repo-local test surfaces live in packages/rn-tester, private/helloworld, and private/react-native-fantom. CI is orchestrated by .github/workflows/test-all.yml.
- Root package.json is private workspace root '@react-native/monorepo' v1000.0.0, pinned to yarn@1.22.22, with workspaces packages/*, packages/react-native-test-library/* and private/* (excluding private/helloworld).
- The published `react-native` package lives in packages/react-native: bin 'react-native' -> cli.js, main ./index.js, and an exports map exposing ./setup-env, ./react-private-interface, ./unstable-internals-do-not-use, ./asset-registry, deep ./Libraries/* imports, and conditional 'types' -> ./types_generated/index.d.ts.
- JavaScript source is split between the legacy location packages/react-native/Libraries and the newer packages/react-native/src/private; AGENTS.md documents the gradual migration, and index.js imports from both trees.
- The shared C++ core lives in packages/react-native/ReactCommon and includes react/renderer (Fabric: uimanager, mounting, scheduler, runtimescheduler, components, dom), react/nativemodule (TurboModules), jsi, jsiexecutor, hermes, yoga (layout engine), jsinspector-modern (CDP stack), callinvoker and cxxreact.
- The Android runtime is packages/react-native/ReactAndroid (Kotlin/Java/JNI): bridgeless entry is com.facebook.react.runtime.ReactHostImpl (manages a ReactInstance with Bolts Tasks); the legacy ReactNativeHost->ReactInstanceManager path still exists but is @Deprecated and logged as LegacyArchitecture.
- The iOS runtime is packages/react-native/React and ReactApple (Objective-C++/Swift); RCTReactNativeFactory is the modern factory entry (initWithDelegate:releaseLevel: sets feature flags, creates RCTRootViewFactory, installs the third-party Fabric components provider), with RCTAppDelegate/RCTDefaultReactNativeFactoryDelegate as app-level glue.
- iOS distribution is via CocoaPods podspecs (React.podspec, React-Core.podspec, React-Core-prebuilt.podspec, React-Fabric.podspec) plus third-party-podspecs, and SwiftPM via packages/react-native/Package.swift with scripts under scripts/spm.
- Android build is Gradle: root build.gradle.kts/settings.gradle.kts plus packages/gradle-plugin (react-native-gradle-plugin with tasks like GenerateCodegenArtifactsTask, GenerateCodegenSchemaTask, BundleHermesCTask, and a settings-plugin ReactSettingsPlugin).
- The dev-toolchain packages are @react-native/community-cli-plugin (start/bundle/codegen/spm commands), @react-native/dev-middleware (createDevMiddleware + InspectorProxy CDP WebSocket proxy), @react-native/metro-config (getDefaultConfig), @react-native/react-native-babel-preset / babel-transformer, and @react-native/debugger-frontend + @react-native/debugger-shell for React Native DevTools.
- Repo-facing apps and runners: packages/rn-tester ('@react-native/tester' — RNTester showcase app with Podfile, RNTesterUnitTests, IntegrationTests), private/helloworld (minimal app, excluded from workspaces), and private/react-native-fantom (headless Hermes/Fabric test runner).
- Primary commands from repo root: yarn test (jest), yarn fantom (scripts/fantom.sh), yarn lint (eslint --max-warnings 0), yarn flow-check, yarn format[-lang], yarn build (scripts/build/build.js compiles packages' src->dist via Babel + flow-api-translator), yarn build-android (./gradlew :packages:react-native:ReactAndroid:build), yarn test-ios (scripts/objc-test.sh).
- Release machinery lives in scripts/releases (set-version.js, prepare-ios-prebuilds.js, dotslash asset upload) and GitHub workflows publish-npm.yml / create-release.yml; test-all.yml's build_npm_package job produces the tarball gating publish.
Feature catalog
User-facing surface = the react-native package's JS API (components + imperative APIs + hooks), the `react-native` CLI commands (start/bundle/codegen/spm via community-cli-plugin), the dev-middleware debugging endpoints, Web-platform APIs (observers, performance), and the published support packages (jest-preset, eslint configs, babel preset, metro-config). Deprecated APIs are still exported behind warnOnce() warnings; several experimental APIs ship under unstable_/experimental_ prefixes.
- Core components exported lazily via getters in index.js: View, Text, TextInput, Image, ScrollView, FlatList, SectionList, VirtualizedList, VirtualizedSectionList, Modal, Pressable, TouchableHighlight/NativeFeedback/Opacity/WithoutFeedback, Switch, RefreshControl, StatusBar, KeyboardAvoidingView, Button, ActivityIndicator.
- Imperative/manager APIs exported: AppRegistry, AppState, Animated, Appearance, BackHandler, DeviceEventEmitter, DeviceInfo, DevMenu, DevSettings, Dimensions, Keyboard, LayoutAnimation, Linking, LogBox, NativeAppEventEmitter, NativeComponentRegistry, NativeEventEmitter, NativeModules, Networking, PanResponder, PermissionsAndroid, PixelRatio, Platform, Settings, Share, StyleSheet, Systrace, ToastAndroid, TurboModuleRegistry, UIManager, Vibration, plus codegenNativeCommands/codegenNativeComponent/requireNativeComponent helpers.
- Deprecated-but-still-exported APIs emit warnOnce() on first access: DrawerLayoutAndroid, ImageBackground, SafeAreaView, InputAccessoryView, ProgressBarAndroid, Clipboard, PushNotificationIOS, UTFSequence — each message points at the community package that replaces it.
- InteractionManager was removed from core: in __DEV__ a definedProperty getter on the export throws an invariant directing users to requestIdleCallback.
- Experimental exports ship behind unstable_/experimental_ prefixes: unstable_VirtualView, unstable_VirtualArray, unstable_createVirtualCollectionView, unstable_VirtualColumn/Row/Generators, unstable_NativeText, unstable_NativeView, unstable_TextAncestorContext, experimental_LayoutConformance, unstable_batchedUpdates.
- Hooks exported: useAnimatedValue, useAnimatedValueXY, useAnimatedColor, useColorScheme, usePressability, useWindowDimensions.
- CLI commands come from @react-native/community-cli-plugin: 'start' (dev server: --port/--host/--https/--key/--cert/--watchFolders/--reset-cache/--no-interactive/--client-logs), 'bundle' (--entry-file/--platform/--dev/--minify/--bundle-output/--sourcemap-output/--assets-dest/--asset-catalog-dest/--unstable-transform-profile), 'codegen' (--path/--platform/--outputPath/--source), and 'spm'.
- Dev middleware mounts '/open-debugger' (openDebuggerMiddleware), serves the React Native DevTools frontend statically at '/debugger-frontend' from @react-native/debugger-frontend, and forwards everything else to InspectorProxy which owns WebSocket endpoints '/inspector/device' and '/inspector/debug' for CDP traffic.
- Web-platform APIs are implemented under src/private/webapis (IntersectionObserver, MutationObserver, ResizeObserver, performance) and installed at startup only when the corresponding ReactNativeFeatureFlags accessor returns true (enableIntersectionObserverByDefault/enableMutationObserverByDefault/enableResizeObserverByDefault).packages/react-native/src/private/setup/setUpDefaultReactNativeEnvironment.js#L45-62packages/react-native/src/private/setup/setUpIntersectionObserver.jspackages/react-native/src/private/setup/setUpMutationObserver.jspackages/react-native/src/private/setup/setUpResizeObserver.jspackages/react-native/src/private/webapis
- Networking surface: setUpXHR installs fetch/XMLHttpRequest polyfills at init; RCTNetworking is the native HTTP module; WebSocket has its own module; whatwg-fetch and ws are runtime dependencies of the react-native package.
- Developer-experience features: LogBox (installed in DEV via LogBox.install() during env setup), DevMenu/DevSettings native-backed modules, HMRClient for Fast Refresh, and React DevTools wiring via setUpReactDevTools and react-devtools-core dependency.
- Consumer-facing support packages published from the monorepo: @react-native/jest-preset, @react-native/eslint-config + eslint-plugin-react-native + eslint-plugin-specs, @react-native/metro-config, @react-native/babel-preset + babel-transformer, @react-native/normalize-color(s), @react-native/polyfills, @react-native/assets-registry/asset-utils, @react-native/virtualized-lists, @react-native/typescript-config.packages/jest-preset/package.jsonpackages/eslint-plugin-react-native/package.jsonpackages/eslint-plugin-specs/package.jsonpackages/metro-config/package.jsonpackages/react-native-babel-preset/package.jsonpackages/normalize-color/package.jsonpackages/polyfills/package.jsonpackages/assets-registry/package.jsonpackages/virtualized-lists/package.json
Data flow
End-to-end: Metro prepends react-native/setup-env to every bundle; setUpDefaultReactNativeEnvironment installs globals then requires AppRegistry; the native side calls AppRegistry.runApplication which resolves the registered runnable and renders through RendererProxy into ReactFabric; Fabric commits to the C++ shadow tree (ReactCommon/react/renderer) and mounts to platform views. Native capabilities are reached through TurboModuleRegistry (__turboModuleProxy with BatchedBridge fallback); native-to-JS calls go through BatchedBridge callFunctionReturnFlushedQueue and DeviceEventEmitter. Native interfaces are declared as JS specs that codegen turns into C++/ObjC++/Java at build time. Feature flags are authored once and generated into JS/Java/C++ accessors.
- Metro serializer getModulesRunBeforeMainModule injects 'react-native/setup-env' before the app entry on every bundle; InitializeCore.js is kept as a deprecated alias that simply requires ../../src/setup-env so it stays in the module graph.
- setUpDefaultReactNativeEnvironment runs a fixed require order — setUpGlobals, setUpDOM, setUpPerformance, polyfillPromise, setUpTimers, [DEV] setUpReactDevTools, setUpErrorHandling, setUpRegeneratorRuntime, setUpXHR, setUpAlert, setUpNavigator, setUpBatchedBridge, setUpSegmentFetcher, [DEV] checkNativeVersion/setUpDeveloperTools/LogBox.install — then requires AppRegistry and conditionally installs the three observers. A module-level `initialized` flag makes it idempotent.
- App launch data flow: JS calls AppRegistry.registerComponent(appKey, provider) which stores a runnable in the `runnables` map; native invokes runApplication(appKey, appParameters) over the bridge; the runnable calls renderApplication which wraps RootComponent in AppContainer (plus optional React.unstable_Activity wrapper) and calls Renderer.renderElement with createRootTag(rootTag).
- RendererProxy re-exports RendererImplementation, which resolves to Libraries/Renderer/implementations/ReactFabric-{dev,prod,profiling}.js; the proxy indirection exists so bundles can swap the Fabric renderer variant (profiling vs production) at runtime.
- TurboModule resolution: TurboModuleRegistry.get/getEnforcing first calls global.__turboModuleProxy(name); on miss it falls back to NativeModules[name] (the BatchedBridge-generated module object); getEnforcing throws an invariant naming the module when neither yields a result.
- Legacy-bridge module calls: NativeModules.genModule builds each method via genMethod (typed 'async'|'promise'|'sync' from the ModuleConfig tuple [name, constants, methods, promiseMethodIDs, syncMethodIDs]) which enqueues onto BatchedBridge/MessageQueue.enqueueNativeCall; RCT/RK prefixes must already be stripped natively (invariant).
- Native -> JS direction: registerCallableModule exposes JS modules to native through BatchedBridge (AppRegistry itself is registered this way at module load, alongside global.RN$AppRegistry); RCTDeviceEventEmitter/NativeAppEventEmitter deliver native events into JS.
- Codegen flow: `Native*.js` and `*NativeComponent.js` spec files -> @react-native/codegen parsers (flow/typescript) -> CodegenSchema -> RNCodegen.generate -> LIBRARY_GENERATORS buckets (componentsIOS, componentsAndroid, modulesIOS, modulesAndroid, modulesCxx, descriptors, events, props, states, shadow-nodes, tests) -> writeMapToFiles or checkFilesForChanges (verify mode when test:true).
- Codegen is invoked three ways: `react-native codegen` CLI -> scripts/codegen/generate-artifacts-executor.execute(path, platform, outputPath, source); Android Gradle via GenerateCodegenSchemaTask + GenerateCodegenArtifactsTask in the react-native-gradle-plugin; iOS via scripts/generate-codegen-artifacts.js inside the pod build phases.packages/community-cli-plugin/src/commands/codegen.js#L44-60packages/react-native/scripts/codegen/generate-artifacts-executor/index.jspackages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenArtifactsTask.ktpackages/react-native/scripts/generate-codegen-artifacts.js
- Feature flags: ReactNativeFeatureFlags.config.js is the single source of truth (common/jsOnly scopes, defaultValue, metadata.purpose, expectedReleaseValue, ossReleaseStage); `yarn featureflags` regenerates the JS (src/private/featureflags/ReactNativeFeatureFlags.js), Java and C++ accessors, which gate behavior like the observers at env setup.
- Type pipeline: Flow-typed JS sources -> `yarn build-types` (scripts/js-api using flow-api-translator) -> per-module .d.ts in types_generated/ (generated at build time, referenced by the exports 'types' condition) plus the committed whole-API snapshot ReactNativeApi.d.ts; `yarn test-generated-typescript` type-checks the result against __typetests__.
- UI commit path: ReactFabric renderElement -> C++ shadow tree under ReactCommon/react/renderer (uimanager/scheduler/mounting/runtimescheduler) -> platform mount: Android FabricUIManager (com.facebook.react.fabric) and iOS RCTComponentViewFactory/RCTMountingManager; the JS FabricUIManager.js module is the JS-side handle. (inferred)
Behavior traces
Three flows were traced end-to-end: (A) JS environment init -> app registration -> first render; (B) `react-native start` dev-server boot from the bin shim through Metro.runServer; (C) a TurboModule/native call plus codegen generation. Ordered function calls are named in each claim.
- Startup A1: Metro bundle -> serializer.getModulesRunBeforeMainModule() returns ['react-native/setup-env'] -> setup-env.js calls setUpDefaultReactNativeEnvironment() once (idempotent via `initialized` flag).
- Startup A2 (ordered): setUpGlobals -> setUpDOM -> setUpPerformance -> polyfillPromise -> setUpTimers -> [__DEV__] setUpReactDevTools -> setUpErrorHandling -> setUpRegeneratorRuntime -> setUpXHR -> setUpAlert -> setUpNavigator -> setUpBatchedBridge -> setUpSegmentFetcher -> [__DEV__] checkNativeVersion, setUpDeveloperTools, LogBox.install() -> require(AppRegistry) -> flag-gated setUpIntersectionObserver/setUpMutationObserver/setUpResizeObserver.
- Startup A3: AppRegistry module load registers the 'LogBox' surface component, sets global.RN$AppRegistry, and registerCallableModule('AppRegistry', AppRegistry) so native can invoke runApplication over the bridge; user code's registerComponent(appKey, provider) stores runnables[appKey] = (appParameters, displayMode) => renderApplication({...}).
- Startup A4: native calls runApplication -> runnable fires renderApplication -> invariant(rootTag) -> builds <AppContainer rootTag WrapperComponent rootViewStyle initialProps internal_excludeLogBox><RootComponent .../></AppContainer> -> optional DEV debugName wrapper via getCachedComponentWithDebugName -> optional React.unstable_Activity wrapper when useOffscreen -> Renderer.renderElement({element, rootTag: createRootTag(rootTag)}).
- Startup A5 (platform): Android ReactActivityDelegate resolves ReactHost (bridgeless) -> ReactHostImpl lazily creates the ReactInstance guarded by Bolts Tasks; the deprecated path builds ReactInstanceManager via ReactNativeHost.getBaseReactInstanceManagerBuilder().setJSMainModulePath(...).addPackage(each getPackages()).build(). iOS: RCTReactNativeFactory.initWithDelegate:releaseLevel: -> _setUpFeatureFlags -> applyDefaultColorSpace -> createRCTRootViewFactory -> startReactNativeWithModuleName:inWindow:.packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java#L87-100packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.kt#L79-121packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt#L86-120packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm#L76-100
- Dev server B1: `react-native` bin -> cli.js -> findCommunityCli() resolves '@react-native-community/cli' from process.cwd() (supports pnpm-style isolated node_modules) -> cli.run('react-native'); when required as a library, a missing community CLI is silently tolerated.
- Dev server B2: community CLI loads react-native.config.js which pushes bundleCommand, startCommand, codegenCommand, spmCommand from @react-native/community-cli-plugin and dynamically resolves @react-native-community/cli-platform-{android,ios} for platform project/dependency config.
- Dev server B3 (ordered inside runDevServer): loadMetroConfig(cliConfig, {...flags}) -> isDevServerRunning(devServerUrl, projectRoot) returning 'matched_server_running'|'port_taken' -> build TerminalReporter (or --custom-log-reporter-path) -> loadCommunityMiddleware().createCommunityMiddleware({host, port, watchFolders}) -> createDevMiddleware({serverBaseUrl, logger}) -> Metro.runServer(metroConfig, {host, secure, secureCert, secureKey, unstable_extraMiddleware: [communityMiddleware, middleware], websocketEndpoints: merged}) -> eventsSocketEndpoint.reportEvent wired into the reporter -> version.logIfUpdateAvailable.
- Dev server B4 (key handlers): when interactive, 'initialize_done' triggers attachKeyHandlers({devServerUrl, messageSocket, reporter}) so pressing keys (e.g. open debugger via OpenDebuggerKeyboardHandler) talks to the dev middleware over the message socket.
- Native call C1: a spec module like NativeDevSettings re-exports the typed interface from src/private/specs_DEPRECATED/modules; callers do TurboModuleRegistry.getEnforcing('DevSettings') -> global.__turboModuleProxy('DevSettings') (JSI-installed proxy) -> fallback NativeModules['DevSettings'] -> invariant throw 'Verify that a module by this name is registered in the native binary.'
- Codegen C2 (ordered): `react-native codegen` -> require.resolve('react-native/scripts/codegen/generate-artifacts-executor/index') -> .execute(path, platform, outputPath, source) -> parsers produce schemas (combine-js-to-schema-cli merges per-library schemas) -> RNCodegen.generate validates each schema via SchemaValidator.validate then fans out to the requested LIBRARY_GENERATORS -> checkOrWriteFiles writes output dirs or diffs in test mode.
- Bundle flow (release): `react-native bundle` -> buildBundle (unstable_buildBundleWithConfig exported for embedders) -> Metro bundle build -> saveAssets writes resolved asset files per platform (getAssetDestPathAndroid / getAssetDestPathIOS / assetCatalogIOS / filterPlatformAssetScales); Metro's transformer.assetRegistryPath is 'react-native/asset-registry' so asset requires become registry calls.packages/community-cli-plugin/src/commands/bundle/index.js#L26-60packages/community-cli-plugin/src/commands/bundle/buildBundle.jspackages/community-cli-plugin/src/commands/bundle/saveAssets.jspackages/community-cli-plugin/src/commands/bundle/assetCatalogIOS.jspackages/metro-config/src/index.flow.js#L85-89
Testing signals
Two JS test systems: Jest 29 unit tests in __tests__/*-test.js (custom preprocessor + haste resolver that strips `exports`, ios/android platforms, fake timers globally) and Fantom *-itest.js integration tests that run JS on Hermes against the real C++ Fabric/mounting stack with a mock host platform — Fantom files must live under packages/react-native. Native testing: Gradle test task for ReactAndroid, scripts/objc-test.sh + RNTesterUnitTests/RCTTest for iOS. CI in test-all.yml gates on lint, test_js, build_js_types, Fantom (with two retry rounds), Android/iOS builds, helloworld e2e, RNTester e2e and Maestro cloud runs.
- Jest config: testRegex '/__tests__/.*-test(\.fb)?\.js$' (the .fb suffix runs Meta-internal tests), haste platforms ['ios','android'] default ios, fakeTimers enabled globally (modern), testEnvironment node, coverage collected from packages/react-native/Libraries/**/*.js and src/**/*.js.
- Jest resolution diverges deliberately from Metro: a custom resolver (packages/jest-preset/jest/resolver.js) strips package.json `exports`, so moduleNameMapper aliases map 'react-native/setup-env', 'react-native/react-private-interface' and 'react-native/unstable-internals-do-not-use' to their src/ files; all sources transform through jest/preprocessor.js.
- Roughly 234 *-test.js files exist under __tests__ dirs (find count) — concentrated in packages/react-native/Libraries and src/private plus per-package suites (community-cli-plugin, dev-middleware, codegen generators/parsers __tests__).
- Fantom (~202 *-itest.js files) is the integration/benchmark runner: tests execute on Hermes with the real C++ Fabric renderer, TurboModules and bridgeless runtime against a mock host platform; API = Fantom.createRoot/runTask/scrollTo/installTimerMock on a deterministic virtual clock; docblock pragmas @fantom_flags, @fantom_mode, @fantom_native_opt, @fantom_js_opt, @fantom_js_bytecode generate test-matrix variants including wildcard '*'.
- Fantom asserts real layout: examples show root.getRenderedOutput({includeLayoutMetrics:true}).toJSX() producing <rn-view layoutMetrics-frame=.../> and element.getBoundingClientRect() returning Yoga-computed metrics — capabilities Jest can't provide; mounting instructions are inspectable via root.takeMountingManagerLogs().
- CI Fantom path: test-all.yml job build_fantom_runner (8-core-ubuntu container reactnativecommunity/react-native-android) then run_fantom_tests uses ./.github/workflows/fantom-tests.yml with two automatic retry rounds (run_fantom_tests_retry_1/2, second round sets fail-on-error: true).
- E2E coverage: e2e-android-rntester.yml / e2e-ios-rntester.yml / e2e-*-templateapp.yml run RNTester and template apps on emulators/simulators; test_maestro_cloud_{android,ios} push rntester release builds to Maestro Cloud on main pushes and same-repo PRs; helloworld e2e jobs (test_android_helloworld, test_ios_helloworld) validate the template path.
- Native unit tests: `yarn test-android` runs ./gradlew :packages:react-native:ReactAndroid:test (JUnit/Robolectric under ReactAndroid/src/test); `yarn test-ios` runs scripts/objc-test.sh against packages/rn-tester's RNTesterUnitTests and RCTTest infrastructure.
- Type/API regression gates: `yarn flow-check` (flow full-check, flow-bin 0.332), `yarn test-generated-typescript` (tsc -p __typetests__/tsconfig.json), js-api-diff job via scripts/js-api/diff-api-snapshot (workflow api-changes.yml), and validate-cxx-api-snapshots.yml for the C++ API snapshots.
- Lint/format gates: `yarn lint` = eslint --max-warnings 0; `yarn format-check` fans out to prettier (js/md/yaml), clang-format (cpp), format-kotlin/format-java/format-python/format-swift scripts, each with pinned toolchain packages; shellcheck runs via .github/workflow-scripts/analyze_scripts.sh; markdownlint via lint-markdown.
- Uncovered/soft spots: testPathIgnorePatterns excludes sdks/hermes and Libraries/Renderer implementations from Jest; Fantom is limited to packages/react-native internals (no app-level testing); C++ sampling profiler (FANTOM_PROFILE_CPP) and JS/C++ debugging (FANTOM_DEBUG_JS/CPP) only work on Meta infrastructure.
Risk map
Dominant risk is the long-running architecture migration: legacy bridge (ReactNativeHost/ReactInstanceManager, BatchedBridge/MessageQueue, runtime codegenNativeComponent fallback) coexists with bridgeless ReactHost + TurboModule proxy, so behavior diverges by mode. Generated-code drift is a second axis — feature flags, TS types, codegen output, and three committed API snapshots all fail CI if not regenerated. The dev-middleware WebSocket inspector endpoints are a security-sensitive surface when the dev server is exposed beyond localhost. Several foot-guns exist for tooling: preinstall mutates yarn.lock, Jest resolution diverges from Metro, and cli.js silently tolerates a missing community CLI.
- Legacy/new architecture split: ReactNativeHost is @Deprecated and logs LegacyArchitectureLogLevel.ERROR; ReactActivityDelegate.getReactNativeHost() is deprecated in favor of ReactHost; code paths still branch on global.RN$Bridgeless (e.g. codegenNativeComponent warns that runtime fallback 'will be an error in the future').
- Runtime/type surface drift: index.js header warns 'Keep this file in sync with index.js.flow' — the runtime getters (incl. the untyped 'Touchable' re-export kept for a react-native-svg call site) can diverge from the declared public API, only caught by generated-types tests.
- Dev-middleware attack surface: InspectorProxy opens unauthenticated WebSocket endpoints '/inspector/device' and '/inspector/debug' proxying full CDP control of connected apps, plus '/open-debugger' which launches local applications via the tool launcher; `start --host`/`--https`/`--cert`/`--key` options determine exposure — dangerous if bound to a non-localhost interface.
- Dynamic resolution fragility: cli.js and react-native.config.js use require.resolve(..., {paths: [process.cwd()]}) for @react-native-community/* packages — correct under pnpm/isolated layouts but silently degrades (empty config, exit(1) with a warning) when the community CLI isn't installed; a module-scope try/catch swallows the missing dependency when cli.js is imported rather than executed.
- preinstall side effect: `yarn install` runs scripts/try-set-hermes-compiler-prebuilt.js which rewrites the hermes-compiler placeholder ('0.0.0') in packages/react-native/package.json and dirties yarn.lock — documented as expected, but it will pollute diffs and confuse automated installs.
- Generated-artifact drift is CI-enforced: codegen output, feature-flag accessors (yarn featureflags), types_generated + ReactNativeApi.d.ts (yarn build-types), scripts/cxx-api/api-snapshots (yarn cxx-api-build/--validate), and ReactAndroid/api/ReactAndroid.api are all regenerated-or-fail; hand-edits get reverted by CI validation.
- Metro bundling hard-depends on a one-time codegen build: without `yarn --cwd packages/react-native-codegen build`, bundling fails with 'Cannot find module @react-native/codegen/lib/parsers/flow/parser' — a documented bootstrap trap for fresh clones.
- Jest fidelity gap: the custom resolver strips `exports` and moduleNameMapper hardcodes subpath aliases, so unit tests exercise a different module-resolution graph than Metro/Node — code relying on export conditions (e.g. types_generated, react-native-legacy-deep-imports) is untested by Jest. (inferred)
- DEV-only behavior divergence: LogBox.install, checkNativeVersion, setUpReactDevTools, InteractionManager-throwing getter, warnOnce deprecation banners, and the RN$Bridgeless codegen warning only run under __DEV__, so prod bundles take meaningfully different code paths (incl. AppContainer-dev vs AppContainer-prod and ReactFabric-dev/-prod/-profiling variants).
- Ecosystem-coupled deprecations: exports for Clipboard, PushNotificationIOS, SafeAreaView, ProgressBarAndroid, DrawerLayoutAndroid, ImageBackground, UTFSequence remain (warnOnce) alongside legacy deep imports './Libraries/*' — removal is a breaking change deferred across releases; InteractionManager already throws in DEV.
- Fantom operational limits: debugging (FANTOM_DEBUG_JS/FANTOM_DEBUG_CPP) is Meta-internal only; FANTOM_PROFILE_CPP needs perf and is banned on CI; runTask() cannot be nested (throws); only a subset of the Jest global API is implemented (no test.each) — contributors must work within these limits or file gaps.
- Large monorepo coupling: the scanned tree is ~3998 files with the file walk capped (1008 skipped); a single change can trigger lint -> build_fantom_runner -> run_fantom_tests (x3 retries) -> build_android -> e2e -> maestro -> build_npm_package chains in test-all.yml, so blast radius of even small changes is expensive CI-wise. (inferred)
Extension points
Extension happens at well-defined seams: JS spec files (Native*.js / *NativeComponent.js) + codegenConfig libraries in package.json feed codegen; TurboModuleRegistry and codegenNativeComponent/requireNativeComponent are the runtime registration points; AppRegistry.registerComponent(+setWrapperComponentProvider/setRootViewStyleProvider) registers root surfaces; community-cli-plugin exports composable Command objects (with unstable_createBundleCommandParser for custom parsers); createDevMiddleware exposes unstable_* hooks; metro-config exposes getDefaultConfig/mergeConfig/setFrameworkDefaults; the Gradle ReactExtension DSL and iOS RCTReactNativeFactoryDelegate/RCTDependencyProvider are the native wiring points; the feature-flag config and Fantom pragmas are repo-internal extension points.
- New TurboModule: author a `Native<Name>.js` spec (typed interface extending TurboModule) and fetch it with TurboModuleRegistry.getEnforcing('<Name>'); the react-native package's own specs are declared in package.json codegenConfig.libraries (FBReactNativeSpec, jsSrcsDir 'src', per-module ios unstableRequiresMainQueueSetup flags).
- New Fabric component: author a `*NativeComponent.js` spec consumed by codegenNativeComponent (options: interfaceOnly, paperComponentName, paperComponentNameDeprecated, excludedPlatforms); codegenNativeCommands generates the imperative command dispatchers; requireNativeComponent remains the runtime lookup.
- App registration seams: AppRegistry.registerComponent/registerConfig/registerRunnable; setWrapperComponentProvider wraps every root component; setRootViewStyleProvider injects root view styles; registerCallableModule exposes a JS module to native callers via BatchedBridge.
- CLI extension: @react-native/community-cli-plugin exports Command objects (bundleCommand, startCommand, codegenCommand, spmCommand) that any CLI config can register; unstable_createBundleCommandParser(additionalOptions) builds a commander parser with the base bundle options plus caller-supplied ones; unstable_buildBundleWithConfig lets embedders invoke bundling programmatically.
- Dev-middleware hooks: createDevMiddleware accepts unstable_eventReporter (structured event sink), unstable_experiments (enableOpenDebuggerRedirect, enableNetworkInspector, enableStandaloneFuseboxShell), unstable_toolLauncher (custom app launcher for the debugger frontend), unstable_customInspectorMessageHandler (per device/debugger-pair CDP handler), unstable_trackInspectorProxyEventLoopPerf.
- Metro config extension: getDefaultConfig(projectRoot) merges Metro defaults + reactNativeDefaults + frameworkDefaults; frameworks inject config via setFrameworkDefaults(); mergeConfig is re-exported for user overrides; resolver sets resolverMainFields ['react-native','browser','main'], platforms ['android','ios'], unstable_conditionNames ['react-native'].
- Android app wiring: subclass DefaultReactNativeHost/ReactNativeHost and return extra ReactPackage instances from getPackages(); ReactActivityDelegate exposes getLaunchOptions/createRootView overrides; the react-native-gradle-plugin exposes a `react {}` DSL (ReactExtension) plus settings-plugin ReactSettingsPlugin for autolinking.packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.kt#L191-197packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java#L64-85packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackage.ktpackages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactExtension.kt
- iOS app wiring: RCTReactNativeFactory takes an RCTReactNativeFactoryDelegate (RCTDefaultReactNativeFactoryDelegate provided); RCTDependencyProvider supplies autolinked dependencies; third-party Fabric components register through RCTComponentViewFactory's thirdPartyFabricComponentsProvider which the factory sets to itself.
- Feature-flag extension: add a definition (common|jsOnly scope, defaultValue, metadata{description,purpose,expectedReleaseValue,dateAdded}, ossReleaseStage, optional skipNativeAPI) in ReactNativeFeatureFlags.config.js and run `yarn featureflags` to regenerate JS/Java/C++ accessors; Fantom tests override flags via @fantom_flags pragmas.
- Consumer-facing presets: @react-native/jest-preset packages the preprocessor/resolver/setup for app tests; eslint-config-react-native + eslint-plugin-react-native + eslint-plugin-specs package the lint surface; @react-native/typescript-config ships tsconfig bases; babel-preset + metro-babel-transformer are the transform seam.
Contributor handoff
Start with AGENTS.md — it is the canonical contributor doc (repo layout, commands, gotchas). JS is Flow; expect paired .js/.js.flow files and code split between Libraries/ (legacy) and src/private/ (target). Verify with yarn test / yarn fantom / yarn lint / yarn flow-check; build @react-native/codegen once before Metro works. Never hand-edit generated artifacts — regenerate via yarn featureflags / yarn build-types / codegen and keep index.js in sync with index.js.flow. Respect the committed API snapshots (ReactNativeApi.d.ts, ReactAndroid.api, scripts/cxx-api) and the yarn.lock churn from the preinstall hook.
- AGENTS.md is the authoritative onboarding doc: it maps every top-level dir, lists yarn commands, documents the codegen-build prerequisite and the preinstall yarn.lock dirtying — read it before editing.
- JS edits go in packages/react-native/src/private for new code (Libraries/ is the legacy location being migrated); all JS is Flow-typed (@flow strict/strict-local pragmas) and many public modules have a .js.flow twin that must be kept in sync (e.g. index.js vs index.js.flow).
- Verification ladder for a JS change: yarn test <path> (Jest, -test.js files in __tests__), yarn fantom <path> (integration, -itest.js — first run builds the native tester), yarn lint (zero warnings allowed), yarn flow-check, yarn format-check-javascript.
- Bootstrap trap: Metro cannot bundle until codegen is built once — run `yarn --cwd packages/react-native-codegen build`; every other package runs from source so `yarn build` is otherwise unnecessary for development.
- Generated code rule: never hand-edit generated output — native specs produce bindings at build time; feature flags regenerate via `yarn featureflags`; TS types via `yarn build-types` + snapshot ReactNativeApi.d.ts; C++ API via `yarn cxx-api-build`; Android API surface is committed at ReactAndroid/api/ReactAndroid.api. CI validates all of them.
- Test placement conventions: Jest tests live in __tests__/ dirs as *-test.js; Fantom integration tests use *-itest.js and must live inside packages/react-native (Fantom is scoped to RN internals); benchmarks use *-benchmark-itest.js; prefer public-API imports ('react-native') over internals in Fantom tests.
- Safe edit boundaries: tooling packages (community-cli-plugin, dev-middleware, metro-config, codegen) are self-contained with their own __tests__; runtime JS changes should respect __DEV__ gating and warnOnce/invariant patterns; native changes require the matching platform toolchain (Xcode+CocoaPods for iOS, Android SDK+NDK+Gradle).
- Known traps: `yarn install` dirties yarn.lock (preinstall hermes-compiler rewrite — do not commit); Jest moduleNameMapper aliases exist only for setup-env/react-private-interface/unstable-internals-do-not-use — new subpath exports need mapper entries or Jest resolution breaks.
- Runtime verification loop: `yarn start` serves RNTester via Metro on http://localhost:8081 (RCT_METRO_PORT overrides); `yarn android` builds+installs it; sanity-check the bundler with curl 'http://localhost:8081/js/RNTesterApp.bundle?platform=ios&dev=true'.
- Architecture docs live in __docs__/ directories beside the code they describe (indexed by root __docs__/README.md) — e.g. feature flags, inspector proxy protocol, runtime scheduler event loop, intersection/mutation observers; treat them as reference for the subsystem being edited.
- PR conventions: keep changes focused (no unrelated refactors/formatting), complete the changelog category/type tags in the PR template (CHANGELOG.md is compiled at release from PR descriptions), and state exact test commands run plus screenshots for UI changes.
Languages
| Language | Files | Share of source |
|---|---|---|
| JavaScript | 2391 | 42% |
| C | 1425 | 25% |
| Kotlin | 951 | 17% |
| C++ | 657 | 12% |
| Markdown | 287 | docs/config |
| TypeScript | 131 | 2% |
| YAML | 102 | docs/config |
| JSON | 83 | docs/config |
Start reading here
Probable source entrypoint: packages/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
packages/ (3093 files)
Source layout — packages/
| Path | Files |
|---|---|
packages/asset-utils/ | 7 |
packages/assets-registry/ | 7 |
packages/babel-plugin-codegen/ | 7 |
packages/community-cli-plugin/ | 32 |
packages/debugger-frontend/ | 4 |
packages/debugger-shell/ | 26 |
packages/dev-middleware/ | 45 |
packages/eslint-config-react-native/ | 4 |
Repository structure
| Top-level path | Files |
|---|---|
packages/ | 3093 |
scripts/ | 511 |
private/ | 205 |
.github/ | 92 |
flow-typed/ | 48 |
.expo-code-review/ | 9 |
__docs__/ | 3 |
gradle/ | 2 |
Docs found: AGENTS.mdCONTRIBUTING.mdREADME.md
Package
Gemfile — , 0 declared dependencies.
CI & config entrypoints
README.md— Repository readmeAGENTS.md— Agent instructions.github/workflows/analyze-pr.yml— GitHub Actions workflow.github/workflows/api-changes.yml— GitHub Actions workflow.github/workflows/autorebase.yml— GitHub Actions workflow.github/workflows/build-android-templateapp.yml— GitHub Actions workflow.github/workflows/bump-podfile-lock.yml— GitHub Actions workflow.github/workflows/cache-reaper.yml— GitHub Actions workflow.github/workflows/check-for-reproducer.yml— GitHub Actions workflow.github/workflows/close-pr.yml— GitHub Actions workflow
Structural health signals
Average score 5.9/10 across 96 analyzed source files; 70 hotspots surfaced.
packages/dev-middleware/src/inspector-proxy/Device.js— 1166 lines, score 4 (Large file, Deep nesting)packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.kt— 829 lines, score 4.5 (Large file, Deep nesting)packages/react-native-codegen/src/generators/components/__test_fixtures__/fixtures.js— 1747 lines, score 5 (Large file, Deep nesting)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
b7c0e4986a022026-09-22 — xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp (#58633)baee2f3dd1822026-09-22 — Add changelog for v0.88.0-rc.2 (#58627)f03f6c2b856e2026-09-21 — Align Kotlin version with the AGP Kotlin Gradle plugin (#58624)509e672f00ea2026-09-21 — Remove init-variables clang-tidy workarounds (#58628)3d93b5a1fa3f2026-09-21 — Remove disabled accessibility manager integration test (#58612)