Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58598
Fixes https://github.com/facebook/react-native/issues/56641
The preset failed in two ways under strict-isolation installs (pnpm and Yarn pnpm-mode): `react-native` was not a declared dependency so `jest-preset.js` could not resolve it, and the `transformIgnorePatterns` rule only matched classic `node_modules` layouts so preset sources shipped untransformed.
- Declare `react-native` as a peer dependency so the installer links it into the preset's scope.
- Move `babel/core` from dependencies to peerDependencies: `babel-jest` peer-depends on it, so it must be provided, but as a direct dependency a strict installer gives the preset its own copy and the consumer's `babel.config.js` presets would then load under a different `babel/core` instance than the consumer's own. A peer keeps one copy, matching how `react` and `react-native` are already declared. `babel/runtime` stays a direct dependency: the preset's sources are compiled with `babel/plugin-transform-runtime` helpers enabled, so the transformed `jest/setup.js` requires `babel/runtime/helpers/*` from the preset's own scope at Jest runtime (verified: removing it makes the pnpm harness fail with `Cannot find module 'babel/runtime/helpers/interopRequireDefault'`).
- Resolve the `babel-jest` transformer from the preset's own scope via `require.resolve('babel-jest')` instead of the bare specifier.
- Match `react-native` sources in `transformIgnorePatterns` at each layout's anchored location - classic `node_modules`, pnpm (`.pnpm/<id>/node_modules/...`), and Yarn pnpm-mode (`.store/<flat>-<protocol>-<hash>/package/...`) - so strict-isolation layouts still transform preset and `react-native` sources. Yarn records the resolution protocol in the store entry name, so the pattern accepts `npm` (which also carries the version) plus `virtual`, `patch`, `file`, `portal`, `link`, `exec` and `workspace`; `yarn patch react-native` is common enough that matching only `-npm-`/`-virtual-` would leave those installs broken. The protocols are listed explicitly rather than accepting any word, so `react-native-reanimated-npm-<hash>` cannot put `reanimated` in the protocol position. The classic layout is matched exactly as before this change: scoped third-party packages whose unscoped name is `react-native` (`sentry/react-native`, `notifee/react-native`), nested directories literally named `react-native`, and real `-suffix` packages all stay ignored.
- Write the `.store` scoped-package segment as `(?:-[^-\/]+)*` rather than `(-[^\/]+)*`. The inner class in the original form could itself consume `-`, so a dash-separated name had exponentially many ways to be partitioned and any near-miss path under `node_modules/.store/react-native-...` forced catastrophic backtracking. Jest evaluates `transformIgnorePatterns` against every candidate file path, so one pathological path could hang a run. Restricting the segment to non-dash characters makes the partition unique and the match linear, with no change to which paths are ignored.
Known limitation: under Yarn pnpm-mode's `.store` layout, a scoped package's slash flattens to a dash, erasing the scope boundary - so third-party `react-native-<scope>/*` packages (e.g. `react-native-async-storage/async-storage`) are indistinguishable from genuine `react-native/*` ones and are also transformed there. Transforming is the safe direction (a miss would ship untransformed sources); the impact is performance-only and confined to Yarn pnpm-mode.
Changelog:
[General][Fixed] - Fix `react-native/jest-preset` failing to resolve `react-native` and to transform preset sources under pnpm and Yarn pnpm-mode installs
Reviewed By: robert68-code, vzaidman
Differential Revision: D119701713
fbshipit-source-id: 9f841197bc087b86ae29e77218e13644eb82913e
Summary:
The iOS prebuild (`node scripts/ios-prebuild`) stages React Native's headers into `packages/react-native/.build/headers` as hard links, and skipped any target that already existed.
When changing the original source files, these hard links can become stale and errors like this can occur:
```
Libraries/LinkingIOS/RCTLinkingManager.mm:91:7: error: use of undeclared identifier 'RCTIsSceneDelegateApp'
```
This is a problem that contributors will see - not regular users, but the fix helps with strange error messages.
## Changelog:
[INTERNAL] [FIXED] - Repair stale staged headers in the iOS prebuild instead of compiling against a previous checkout's copies
Pull Request resolved: https://github.com/react/react-native/pull/58596
Test Plan:
✅ New unit tests, 16 cases in `packages/react-native/scripts/ios-prebuild/__tests__/setup-test.js`, using real temporary directories rather than an `fs` mock, since the defect is about inodes.
End to end on an Xcode 27 checkout:
- Replaced a staged header with a copy, so it kept the same contents but a different inode. `node scripts/ios-prebuild -s -f Debug` restored it to the source inode and logged `Linked React/Base → .build/headers/React`. The previous code skipped it.
- Ran setup a second time with nothing changed: no file was relinked, and nothing was logged.
- Confirmed the three colliding targets still resolve to the same source as before the change, by inode.
- `node scripts/ios-prebuild -b -f Debug -p ios-simulator` → `** BUILD SUCCEEDED **`.
Reviewed By: cipolleschi
Differential Revision: D120740409
Pulled By: shwanton
fbshipit-source-id: 41c1eb112c3395d6c7158d7151be7fdd92a21cef
Summary:
`VirtualizedList`/`FlatList`/`SectionList` re-run a small amount of
bookkeeping on every render and on every scroll event (60–120 Hz on
ProMotion displays). This PR removes three allocations from those hot
paths without changing any observable behavior:
1. **`VirtualizedList.render` no longer builds a `Set` for
`stickyHeaderIndices` on every render** when the prop is not provided
(the common case). The Set is now only created when the prop is
present; the two `.has()` lookups use optional access.
2. **`ChildListCollection.forEach` returns early when there are no nested
child lists** (the common case) instead of allocating a
`Map.values()` iterator. This is called from `_onScroll` and the four
other scroll callbacks on every scroll event.
3. **`_orientation()` caches its result** and only rebuilds the object
when the `horizontal` prop changes. `I18nManager.isRTL` is a
module-load constant (only changes on app reload), so the cache is
invalidated solely by the `horizontal` prop. The object is replaced,
never mutated, which keeps `ListMetricsAggregator`'s field-based
invalidation correct.
## Changelog:
[GENERAL][CHANGED] - Reduce allocations in the `VirtualizedList` render and scroll path (avoid per-render `Set` allocation for `stickyHeaderIndices`, per-scroll-event `Map` iterator for the empty nested-list collection, and per-call `orientation` object allocation)
Pull Request resolved: https://github.com/react/react-native/pull/58593
Test Plan:
- `yarn test packages/virtualized-lists` → 9 suites, 186 passed, 69
snapshots:
- `ChildListCollection-test.js` (new): forEach over populated/empty
collection, removal, `forEachInCell`/`anyInCell`
- `VirtualizedList-test.js`: `stickyHeaderIndices` not forwarded when
the prop is absent (with `ListHeaderComponent`), forwarded when
provided; orientation cache identity + invalidation on
`horizontal` change
- `yarn flow-check` → 0 errors
- `yarn lint` → 0 errors, 0 warnings
- `yarn format-check` (changed files)
Micro-benchmark (Node v24, V8, 2M iterations, before vs after, same
machine; the real-world benefit is dominated by reduced GC pressure,
which is largest on low-end Android):
Reviewed By: Abbondanzo
Differential Revision: D121177373
Pulled By: javache
fbshipit-source-id: 1befd7e6d90fbd1fcea9c4cc8cf6d575c6c26111
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58649
Whitespace-only fix. The file was added unformatted in d38c8495e6e2, which makes the react/react-native Format check (yarn format produces a diff) fail on every PR until fixed.
Changelog: [Internal]
___
Reviewed By: javache
Differential Revision: D121381554
fbshipit-source-id: 78a164e006ffe324c8d85e9645c552f7661061ae
Summary:
After https://github.com/react/react-native/pull/58571, the iOS frameworks build started failing due to missing search paths. This PR adds them, which should fix the build.
Changelog: [INTERNAL]
Pull Request resolved: https://github.com/react/react-native/pull/58646
Test Plan: Checks should be green
Reviewed By: cortinico
Differential Revision: D121375796
Pulled By: j-piasecki
fbshipit-source-id: 5a6fa54576584fc4742cfd71db417249393f283e
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58444
Add direct-deep-link Maestro coverage for RNTester native interop scenarios and expand the legacy native module flow to validate its platform-specific result set. Make deep-link flows reliable by cold-starting their first route, acknowledging the iOS system confirmation, waiting for route-specific readiness, and using target-driven list scrolling. Extend the Maestro Cloud execution window for the expanded suite and exercise the intended native methods. Exclude the local screenshot-baseline flow from Maestro Cloud.
Reviewed By: Abbondanzo
Differential Revision: D119484762
fbshipit-source-id: 4004cdfd84f85420eb9e28a4fb9e2f0312ea05e0
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58482
Update the existing Pressable and Modal Maestro flows to open their RNTester examples directly. Extend the Pressable flow to cover content presses, feedback events, hit slop, and text presses.
Changelog: [Internal]
Reviewed By: Abbondanzo
Differential Revision: D119484761
fbshipit-source-id: 54bed143962b1c40e6c18ba2ba75eb965281f744
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58481
Add Maestro coverage for FlatList and SectionList viewability behavior using direct RNTester example deep links. Use explicit gestures inside the virtualized lists and wait for the callback output. Add nonvisual, route-specific readiness IDs so successive deep links cannot match the previously mounted list.
Changelog: [Internal]
Reviewed By: Abbondanzo
Differential Revision: D119484760
fbshipit-source-id: 4fccf77355265728b7574f53600105de7ff34daa
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58480
Add Maestro coverage for portable RNTester interaction scenarios using direct example deep links. The flows cover alerts, animations, appearance, filters, text input, and touchables without screenshot baselines.
Changelog: [Internal]
Reviewed By: Abbondanzo
Differential Revision: D119484759
fbshipit-source-id: f17aa255cdbf860ac9864185b509ddddb34089e5
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58626
Move `NativeSampleTurboModule` into RNTester so React Native core codegen no longer treats the sample as public API. Retarget the sample native implementations to RNTester's generated `AppSpecs` on Android, iOS, and macOS.
Changelog:
[General][Removed] - Remove the sample TurboModule from React Native's public API
Reviewed By: christophpurrer, javache
Differential Revision: D121032097
fbshipit-source-id: 865222ce4d4d4c8da323ae444eb43bec940ef000
Summary:
Make the corrected parent-tag path unconditional and remove obsolete flag plumbing now that the behaviour is fully enabled.
Changelog: [Internal]
Reviewed By: jehartzog
Differential Revision: D121181685
fbshipit-source-id: bf2d3fc99734df51ed37404bfd628a12ed946c2b
Summary:
When trying to use the `react-native/eslint-config` with ESLint v9, we get an issue with the `eslint-plugin-ft-flow`:
```bash
Oops! Something went wrong! :(
ESLint: 9.39.2
TypeError: Error while loading rule 'ft-flow/define-flow-type': context.getAllComments is not a function
```
Looking into it, I noticed that our current version (`^2.0.0`) does not support ESLint v9. Only in version `^3.0.11` it show as a valid peer dependency version.
So, although we do have a flat config, we don't support ESLint v9 out of the box.
## Changelog:
<!-- Help reviewers and the release process by writing your own changelog entry.
Pick one each for the category and type tags:
[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message
For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[General] [Fixed] - Fixed the issue when using the flat config with ESLint v9.
Pull Request resolved: https://github.com/react/react-native/pull/55628
Test Plan:
I've made a repro repo: https://github.com/tcK1/Repro-ESLint9-RN
Updated the ESLint version to v9 and added the `eslint-plugin-ft-flow` version in the `resolutions` configuration in the `package.json`.
In this state, running `yarn eslint .` works as expected. To reproduce the issue, remove the `resolutions` field (and run `yarn install` again to update the dependencies) and run the command again.
Reviewed By: cipolleschi
Differential Revision: D121042705
Pulled By: cortinico
fbshipit-source-id: fd330a514ad991de379ddadc2295190e75ccb663
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58571
Route public C++ dependencies through their supported umbrella entry points so framework consumers can use the text module with strict API enforcement enabled.
Changelog: [Internal]
Reviewed By: cortinico
Differential Revision: D120529435
fbshipit-source-id: 5f0b698acd9a0363c3f56d1a0b9831c27c31fad3
Summary:
Building an app in Xcode fails when it autolinks a library that ships its own `Package.swift`, with one error per such library:
```
Missing package product 'reactnativeskia_ReactNativeSkia.ReactNativeSkia'
Missing package product 'reactnativesafeareacontext_ReactNativeSafeAreaContext.ReactNativeSafeAreaContext'
```
The same project builds fine from the command line. Two problems combine.
**The sync destroys package roots Xcode has already loaded.** Autolinking writes one symlink per self-managed library into `build/generated/autolinking/libs/<Name>`, and Xcode treats each as a local Swift package root. The generator deleted that whole directory and recreated it on every run, even when the generated output was byte-for-byte identical. Measured on a real app, across one no-op sync: the `libs/` inode changed from 1913513750 to 1913930645, `libs/ReactNativeSkia` from 1913513937 to 1913930649, while the generated `Package.swift` kept the same MD5. Recreating a package root that Xcode has already resolved is what produces the error above.
**Xcode's own bookkeeping made the sync run every time.** The "Sync SPM Autolinking" build phase re-syncs when a watched input looks newer than its stamp, and it checked each library's whole directory with `find <dir> -newer <stamp>`. Xcode writes its per-user scheme state inside that directory, at `<lib>/.swiftpm/xcode/xcuserdata/<user>.xcuserdatad/xcschemes/xcschememanagement.plist`. So Xcode's own write marked the next build stale, which triggered the destructive re-sync, which broke that build. `xcodebuild` does not write that file, which is why command-line builds were never affected.
This change makes the `libs/` tree idempotent — unchanged entries keep their inode, and entries that are no longer autolinked are pruned instead of wiped — and makes the staleness check skip `.swiftpm`.
### How to verify
In an app that autolinks a library shipping its own `Package.swift`, build in Xcode twice in a row. Both builds should succeed. Before this change the second build fails with `Missing package product`.
## Changelog:
[IOS][FIXED] - Stop SwiftPM autolinking from recreating library package roots on every sync, which broke Xcode builds of apps using libraries that ship their own Package.swift
Pull Request resolved: https://github.com/react/react-native/pull/58597
Test Plan:
**Unit tests.** `yarn test packages/react-native/scripts/spm` — 21 suites, 1009 tests pass.
New tests, written and seen failing before the fix:
- Two consecutive generation runs over an unchanged self-managed dependency keep both the `libs/` inode and each entry's inode. Failed before the fix with the same inode churn measured on the real app.
- A dependency removed between two runs leaves no symlink under `libs/`.
- The emitted staleness snippet is extracted from the generated script and executed under `/bin/bash -c` with `set -euo pipefail` against a temporary tree. A write under `.swiftpm/` is ignored; a real source change is still detected.
**Real app.** A React Native 0.87.1 app on Xcode 27 autolinking `shopify/react-native-skia` and `react-native-safe-area-context`, both self-managed. Before: `xcodebuild` succeeded, Xcode failed in about 5 seconds with the two errors above, on nearly every build.
**Not run:** the full CI matrix, and no Android-side check — this touches iOS SwiftPM tooling only.
## Scope
Deliberately minimal.
One case still re-syncs: the first time Xcode creates `.swiftpm` inside a library, that bumps the library directory's own mtime, so the build after a fresh checkout reports stale once per library. That is harmless now that the sync is idempotent.
One related item is left alone: the aggregate `Package.swift` is still rewritten on every sync even when its content is unchanged, which bumps its mtime and can make Xcode re-resolve. That is wasteful but not destructive, and it is no longer reached on an ordinary IDE build now that `.swiftpm` writes do not mark the sync stale.
Reviewed By: cipolleschi
Differential Revision: D120740029
Pulled By: shwanton
fbshipit-source-id: 3783d4cee4791ab13517d8f0dcac71f752cb7c1b
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58615
Changelog: [Internal]
The current behavior, merge on the main thread, is kept behind the new `enableFabricCommitBranchingMergeOnMainThread` flag.
Reviewed By: rubennorte
Differential Revision: D120981616
fbshipit-source-id: 47f00ddc150f154481cc908548f39662abf32cbf
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58373
Pull Request resolved: https://github.com/react/react-native/pull/58032
Rolls the umbrella-header + include-guard mechanism across the
`react/renderer/graphics` module, classifying the target as public.
- Adds `<React/Graphics.h>`, re-exporting the module's public interface headers.
- Adds `<react/cxxstableapi/UmbrellaGuard.h>` to 24 root headers and all 18
platform headers.
- Wires the umbrella header directory into the Buck, CMake, CocoaPods, Gradle,
and iOS prebuild header configurations.
`conversions.h` and `Geometry.h` are deliberately left unguarded. Both are
deprecation shims whose only content is a `#warning` redirecting to their
replacements.
`PlatformColorParser.h` and `fromRawValueShared.h` include
`react/renderer/core/RawValue.h`, which was only reachable on Apple platforms
because `react/renderer/core:rawValue` was restricted to `platforms = APPLE`.
No source inside the graphics target included those headers off-Apple, so the
breakage was latent and invisible; reaching them through the umbrella surfaces
it.
Changelog:
[Internal]
Reviewed By: javache
Differential Revision: D116779866
fbshipit-source-id: 22a118acc4b0fc2e19d1da605acd9ee926905cd5
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58622
Source-built CocoaPods frameworks do not automatically expose the directories behind stable <React/...> umbrella includes. Exported and generated headers consume umbrellas owned by sibling frameworks, while consuming application and pod targets do not automatically inherit the header roots for those frameworks.
Add source-only aggregate paths for the umbrella frameworks used by FabricComponents and React-perflogger, configure the React-timing dependency of React-perflogger with its framework header root, and expose React-bridging to generated module targets. Keep prebuilt RNCore unchanged so its <React/...> headers continue to resolve from the prebuilt React.framework.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120992289
fbshipit-source-id: 955d223e99df6fc26341d52ffcd1c1580e300c50
Summary:
AGP 9.2.1 depends on Kotlin Gradle plugin 2.2.10 (see https://developer.android.com/build/releases/agp-9-2-0-release-notes#compatibility), so every build that applies AGP already runs
Kotlin 2.2.10, including this repo (`./gradlew buildEnvironment` shows
`kotlin-gradle-plugin:2.2.0 -> 2.2.10`). The version catalogs still declare `kotlin = "2.2.0"`.
Related Expo fix: https://github.com/expo/expo/pull/50455
## Changelog:
[ANDROID] [CHANGED] - Bump Kotlin to 2.2.10 to match the version required by AGP 9.2.1
Pull Request resolved: https://github.com/react/react-native/pull/58624
Test Plan: - run RN tester ✅
Reviewed By: javache
Differential Revision: D121073295
Pulled By: Abbondanzo
fbshipit-source-id: 33f077242802c1064693ecae16f8decfcaee1762
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58612
Remove the RNTester accessibility manager integration test. The native harness has kept it disabled because the underlying native module is unavailable, and its setup depends on a private native-module API.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120923184
fbshipit-source-id: c75a284f6ee2e5f6a55e293338d858c6f42c56ef
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58605
Use global DOM APIs, supported React Native entry points, and package-relative implementation imports throughout Fantom. Use the renderer-only private interface for the existing public-instance conversion helpers instead of importing Node internals.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120717572
fbshipit-source-id: 1aae11b7b9434c783d42ca289e2c6edd17aa887c
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58603
Generate view configs and Codegen fixtures using supported React Native entry points or values derived from their exports. Generated view config modules can be emitted into arbitrary consumer package locations, so they cannot use a package-relative path back into React Native. Keep `ConditionallyIgnoredEventHandlers` centralized and expose it from the explicitly unstable compatibility entry point instead of duplicating its platform-specific semantics in generated output.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120717571
fbshipit-source-id: 9f3ea06d393eb8702bbb3ef2767c2d7c5fde1315
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58602
Use the React Native root and supported secondary entry points for public APIs used by the Jest preset. Resolve implementation modules through package-relative paths so the preset does not depend on unsupported package subpaths.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120717568
fbshipit-source-id: d19036571e9d34938a4d616303e36548f508ea96
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58618
Expose the renderer proxy as `Renderer` from `react-native/unstable-internals-do-not-use`. Generated Codegen modules can be emitted into consumer packages and need a package entry point for dispatching renderer commands without deep imports.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120991150
fbshipit-source-id: 5808de3dc56d8b0e80bbe4134f740a647ff6151f
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57974
Adds a mechanism for callers to configure `babel/plugin-transform-runtime`
programmatically via Babel caller data, in addition to preset options.
- `enableBabelRuntime` (boolean toggle, or a string to pin a specific
`babel/runtime` version) can now be read from the Babel caller.
- `babelRuntimeModuleName` (the module helpers are imported from) can be
provided via preset options or the Babel caller.
Both values are passed as separate primitives (Babel only permits primitive
caller values). Preset options take precedence over caller data, consistent
with how `unstable_transformProfile` is resolved.
Changelog:
[General][Added] - Allow `react-native/babel-preset` to read `enableBabelRuntime` and `babelRuntimeModuleName` from Babel caller data
Reviewed By: vzaidman
Differential Revision: D114070686
fbshipit-source-id: c65928adc0886805fa56f944f7fff50a30ae268b
Summary:
Artifact uploads can fail on transient network errors such as the `ETIMEDOUT` in https://github.com/react/react-native/actions/runs/35598907154/job/106330670539.
This change:
- adds a local composite action backed by `actions/upload-artifact` v7.0.1
- retries failed uploads twice, waiting 10 seconds before attempt 2 and 20 seconds before attempt 3
- propagates the third failure to the caller
- preserves all v7 inputs and outputs
- migrates all 35 artifact upload call sites to the wrapper
## Changelog:
[INTERNAL] [FIXED] - Retry artifact uploads to reduce transient CI failures.
Pull Request resolved: https://github.com/react/react-native/pull/58620
Test Plan:
- `node_modules/.bin/prettier --check $(git diff --name-only HEAD^ -- '*.yml' '*.yaml')` — passed
- `npx --yes action-validator/cli@0.6.0 .github/actions/upload-artifact/action.yml` — passed
- `git diff HEAD^ --check` — passed
- Verified no `actions/upload-artifact@v6` references remain under `.github`
Reviewed By: andrewdacenko
Differential Revision: D121002745
Pulled By: cortinico
fbshipit-source-id: e63e30f3e86b0a2cb6c94aa549cdfe5e6dfbdc2b
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58613
Automatically mock `NativeAnimatedHelper` in the React Native Jest preset and add a regression test that verifies the helper is mocked.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120986825
fbshipit-source-id: 5a509a92ea8d699e331224c07b408cfd01c3ad0f
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58570
Route public C++ dependencies through their supported umbrella entry points so framework consumers can use the modal module with strict API enforcement enabled.
Changelog: [Internal]
landed-with-radar-review
Reviewed By: cortinico
Differential Revision: D120528333
fbshipit-source-id: 2e37837eae930f117f74101b10379ba73c690858
Summary:
Apply the repository C++ formatter to `RCTSurfacePresenter.mm`. The lambda parameter added in https://github.com/react/react-native/issues/58530 was indented one column too far, causing the `format` job on `main` to produce a tracked diff and fail.
## Changelog:
[INTERNAL] [FIXED] - Fix formatting in RCTSurfacePresenter
Pull Request resolved: https://github.com/react/react-native/pull/58617
Test Plan:
- `yarn format-cpp` — completes successfully and changes only `packages/react-native/React/Fabric/RCTSurfacePresenter.mm`.
- `node ./scripts/clang-format.js --check packages/react-native/React/Fabric/RCTSurfacePresenter.mm` — passes.
- `git diff --check` — passes.
- `yarn format-check-cpp` — the repository-wide check still reports pre-existing violations in vendored Hermes sources; the targeted check above passes for the changed file.
Reviewed By: cipolleschi
Differential Revision: D120989136
Pulled By: cortinico
fbshipit-source-id: ccda2f291ad60e9de7b5fffd153f939102dea238
Summary:
The format workflow currently runs on `macos-15`, whose default Xcode 16.4 toolchain does not meet the repository formatter's Swift 6.3 minimum. As a result, `format-swift.js` warns and skips Swift files.
Run the format job on `macos-26` and explicitly select Xcode 26.6.0 so that `swift format` 6.3 is available and Swift formatting is actually exercised in CI.
This does not include the unrelated C++ formatting change currently making the job red on `main`.
## Changelog:
[INTERNAL] [FIXED] - Configure Swift formatting in the format workflow
Pull Request resolved: https://github.com/react/react-native/pull/58616
Test Plan:
- `yarn prettier --check .github/workflows/format.yml` — passes.
- `swift format --version` — reports `6.3.0` locally.
- [GitHub format run](https://github.com/react/react-native/actions/runs/35590873668/job/106304725401) — the macOS 26 runner accepted Xcode 26.6.0, and `format-swift.js` completed without the missing Swift 6.3 warning. The job remains red because enabling the formatter exposes existing formatting changes (along with the unrelated C++ formatting issue already present on `main`).
Reviewed By: cipolleschi
Differential Revision: D120988250
Pulled By: cortinico
fbshipit-source-id: f21ae2b52041edf84bf142c770ffe0da2821654a
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58558
Generate TurboModule interfaces using the public Bridging C++ entry point so generated consumers remain compatible with the strict API boundary.
Export the bridging module include root from its CMake target so source-built consumers can resolve <React/Bridging.h>.
Changelog:
[Internal]
Reviewed By: cortinico
Differential Revision: D120357994
fbshipit-source-id: 072d67197daf19acffbb66c110833a10a7094cfb
Summary:
`EventEmitter::experimental_flushSync` only *requests* an event beat; the beat is processed at the next `EventBeat::induce`. On iOS the run loop observer that induces the beat runs before Core Animation's commit observer, so a request made from `layoutSubviews` — inside CA's commit cycle — is only processed one frame later. Anything that reports layout-driven state to JS synchronously (`VirtualView` mode changes, and safe area insets in the PRs that build on this) renders a frame late in exactly the cases that matter.
`AppleEventBeat` now also schedules an induce in the **display phase of the current commit cycle**. Core Animation runs a commit as layout → display → commit, so a zero-sized layer marked as needing display during layout gets its `display` call after the whole layout pass and before the transaction is committed. That layer needs to live in the tree being committed, so the beat has to know which tree that is — and the emitter tells it:
- `experimental_flushSync` carries the **tag of the emitting view** through `EventDispatcher` and `EventQueue` to `EventBeat::requestSynchronous(Tag)`, with `kNoTag` (https://github.com/react/react-native/issues/58531) meaning no view attribution; a no-argument overload keeps unattributed requesters and the existing tests unchanged. The emitter reads the tag from its `ShadowNodeFamily` at flush time; `kNoTag` if the family is already gone.
- `AppleEventBeat` resolves the tag to the layer of the view's **window** through a resolver injected by `RCTSurfacePresenter` (`findComponentViewWithTag:` on the mounting registry — nullable, non-creating, main thread) and attaches its flusher layer there. The requesting view's window is by definition the root of the layer tree whose layout emitted the request, so the flusher is guaranteed a display phase in the current commit cycle — including for content UIKit mounts in a window of its own, like a full screen modal or LogBox. Requests within one cycle coalesce into a single induce.
One related fix in `EventBeat` itself: a synchronous request is no longer stranded behind an already-scheduled asynchronous beat (it would silently lose its this-frame guarantee, and the leftover flag would make an unrelated later beat blocking). `AppleEventBeat.cpp` becomes `.mm` for the Objective-C.
**Risk:** this changes when queued events are flushed on iOS for every `experimental_flushSync` caller — today `VirtualView`, and safe area insets with the PRs on top. The worst case is a beat processed a frame *earlier* than before, inside a Core Animation commit; the run loop observer path is untouched and still catches anything the display phase misses (an emitter with no tag, an unmounted view, a request off the main thread). Android ignores the tag. Revert is self-contained.
## Design Q&A:
**What happens when two views in different windows update at once?**
Each requesting window gets its own dirty flusher layer (the map is keyed by host layer), and the first `display` to fire induces the beat, which drains the whole event queue — every window's updates mount before that commit presents. The remaining flushers hit the `isEventBeatRequested_` guard and no-op, so it is one beat total, not one per window. If windows ever commit in separate transactions, each request still resolves within its own window's cycle, since its layer sits in the tree that emitted it. Only requesting windows carry a dirty layer.
**Can the tag point at the wrong view — after an unmount, or a recycled view?**
No. The tag comes from the emitter's `ShadowNodeFamily`, and a family keeps one tag for its whole life, across clones and state updates; if the family is already gone the flush carries `kNoTag` and skips the resolver. What changes over a view's life — its window — is read live: the tag resolves to a view at flush time and `view.window.layer` is looked up then, so a view that moved between windows targets its current tree. A view mid-unmount or recycled resolves to nil (the registry erases the entry and recycled views get tag `0`) and degrades to run-loop-observer timing. The tag only ever influences *where the induce is scheduled*, never what is delivered or to whom, so the blast radius of any staleness is one frame of timing, not correctness.
**Does `VirtualView` need changes to benefit?**
No — its sync mode-change flush goes through its own emitter, so the tag attribution is automatic. The case this improves is a mode change emitted during Core Animation layout (a resize pulling a virtualized item into view): on `main` that renders one frame late; here the induce lands in the display phase of the VirtualView's own window, including inside a full screen modal.
## Changelog:
[INTERNAL] - Process synchronous event beats in the frame that requested them on iOS, scheduling the induce on the requesting view's window
Pull Request resolved: https://github.com/react/react-native/pull/58530
Test Plan:
New unit tests in `EventBeatTest.cpp` cover the beat semantics: a synchronous request during an already-scheduled asynchronous beat, coalescing, and induce ordering. They drive the protected `induce` through a subclass standing in for the platform.
On device, with the safe area insets prop from the PRs above merged on top: an RNTester example renders a loud marker (yellow background) while a view observes the safe area but has not received an inset event yet, so any presented marker frame means the dispatch was not synchronous. The full apply → landscape → portrait sequence **inside a full screen modal** on an iPhone 17 Pro simulator, decomposed with ffmpeg into 982 frames and every frame scanned for the marker color — **zero marker frames**, and mid-rotation frames already carry the incoming orientation's insets, so the padding animates with the rotation. Scoped honestly: the first inset event after setting the prop is processed at the call site, so the marker primarily proves no regression; the same-frame path for layout-driven changes rests on the by-construction argument above plus the rotation frames.
https://github.com/user-attachments/assets/0f2db837-c9c0-4457-96c2-847b7aecf10e
`yarn fantom .../ViewSafeAreaInsets-itest.js` passes 4/4 with the prop merged on top. C++ API snapshots regenerated (`scripts/cxx-api/parser`, Doxygen 1.16.1): the deltas are the `requestSynchronous` overload pair, `EventEmitter::getTag`, the resolver type, and the `AppleEventBeat` constructor and destructor.
---
**Stack** — split out of https://github.com/react/react-native/issues/57967, which stays open as the prototype and design discussion. GitHub will not take a fork branch as a pull request base, so each of these targets `main` and its diff contains the ones below it until they merge. Each PR is one commit on top of the previous one.
This is the bottom of the stack, so its diff is already just this change.
👉 1. https://github.com/react/react-native/issues/58530 — Process synchronous event beats in the frame that requested them
2. https://github.com/react/react-native/issues/58109 — Add an `experimental_onSafeAreaInsetsChange` view prop
3. https://github.com/react/react-native/issues/58110 — Report the window safe area insets through Dimensions
4. https://github.com/react/react-native/issues/58112 — Render the internal SafeAreaView from the safe area insets prop
5. https://github.com/react/react-native/issues/58113 — Remove the native SafeAreaView and the deprecated public export
An earlier variant that targeted the surface's root view instead of the view's window was closed in https://github.com/react/react-native/issues/58528; its review thread carries the analysis behind the window-based resolution. https://github.com/react/react-native/issues/58108 was the per-window predecessor this supersedes.
Reviewed By: javache
Differential Revision: D120200496
Pulled By: Abbondanzo
fbshipit-source-id: b06ecb30935837abd6561f54674afef0cdf215a8
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58532
Changelog: [Internal]
Update the reactperflogger module to use `React/Timing.h` umbrella include instead of a direct one.
Reviewed By: cipolleschi
Differential Revision: D120116155
fbshipit-source-id: 4a9112a57c05ad91dd7c7da5a54b2fecee08ef56
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58548
The umbrella headers under `ReactCommon/**/React/` were only linked into their nested `ReactCommon/...` include dirs during the iOS SPM prebuild, so `#include <React/Debug.h>` (and the other umbrella headers) could not be resolved by the `React` framework, breaking the build.
This diff links each umbrella header directory into the `React` header dir.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D120310677
fbshipit-source-id: b741585ba579b3e109088cfd8a67898487eace42
Summary:
Removes the root `CLAUDE.md`, which only contained `AGENTS.md`. As of [Claude Code v2.1.277](https://github.com/anthropics/claude-code/releases/tag/v2.1.277), Claude Code reads `AGENTS.md` directly in a project with no `CLAUDE.md`, so the import shim is redundant. Keeping a single instruction file avoids the two drifting apart.
Scope: only the root `CLAUDE.md`. `AGENTS.md` and `packages/react-native-compatibility-check/AGENTS.md` are unchanged.
## Changelog:
[INTERNAL] - Remove redundant root CLAUDE.md in favor of AGENTS.md
Pull Request resolved: https://github.com/react/react-native/pull/58600
Test Plan: `git grep CLAUDE.md` returns no references to the file anywhere in the repo. No code or tests are touched.
Reviewed By: christophpurrer
Differential Revision: D120809883
Pulled By: fabriziocucci
fbshipit-source-id: c699b3c7bba9d26869dfab83cd5f8fa1b881aef3
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58607
`onHWKeyEvent` tells JS which key was pressed but not when it was pressed, so a listener can only time a press from the moment the device event reaches the JavaScript thread. That delivery is asynchronous, so any latency measured from JS silently excludes the native-to-JS hop — and excludes more of it the busier the JS thread is, which is exactly when the interaction is slowest.
Add the originating `KeyEvent.getEventTime()` to the event payload. It is `SystemClock.uptimeMillis()`, the same `CLOCK_MONOTONIC` base that `performance.now()` reads in JS, so a listener can subtract the two directly with no clock conversion. The field is omitted for the focus and blur events, which have no originating hardware event.
Additive and behaviour-preserving: no existing payload key changes, and nothing in the framework reads the new one.
## Changelog
Changelog: [Android][Added] - Add `eventTime` to the `onHWKeyEvent` device event payload
Reviewed By: rozele
Differential Revision: D120451212
fbshipit-source-id: a58d239d68071fbe7cd0a4310b1c8be4951cddbb
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58599
`AccessibilityState::selected` was a plain `bool` defaulting to `false`, so the
native side could not tell a component that is selectable but currently
unselected (`accessibilityState={{selected: false}}`) from one that is not
selectable at all (`accessibilityState={{}}`). Both arrived as `false`. The JS
type is already `selected?: ?boolean`, so this is the bridge discarding a value
the public API accepts.
Make `selected` a `std::optional<bool>` defaulting to `std::nullopt`.
JS accessibilityState native selected (before -> after)
{} false -> undefined
{selected: false} false -> false
{selected: true} true -> true
This aligns the representation with ARIA, which `accessibilityState` mirrors:
the bool / tri-state split now tracks which ARIA attributes admit an undefined
value.
field ARIA value type admits undefined representation
disabled boolean no bool
busy boolean no bool
selected boolean yes std::optional<bool> (changed)
expanded boolean yes std::optional<bool>
checked tristate yes CheckedState (None = unset)
That is also why `disabled` and `busy` stay plain `bool`: ARIA gives them no
undefined value, so there is no unset state to preserve. `expanded` was made
optional for this same reason in
https://github.com/facebook/react-native/pull/40881 and `checked` has always
carried a `None`; `selected` was the outlier.
Nor is "unset" merely "absent" for this attribute. `testing-library/dom`
computes it as `boolean | undefined`, documented "false/true if (not)selected,
undefined if not selectable" -- the same shape, with the same meaning, that
this change introduces.
Host platforms need the distinction: on Windows a selectable component must
implement ISelectionItemProvider so UIA can report selection state, and with
the old representation every component carrying an accessibilityState looked
selectable.
iOS and Android rendering is unchanged. Trait derivation coalesces the optional
with `value_or(false)`, and the Android serializer omits the key when the value
is unset, which `BaseViewManager#setViewState` already handles by falling back
to `setSelected(false)`.
Reviewer note: `std::optional<bool>` is contextually convertible to `bool`, so a
bare `if (state.selected)` still compiles but tests engagement rather than
value, silently marking an explicitly unselected component as selected. There is
a regression test for that specific hazard.
Fixes https://github.com/facebook/react-native/issues/46988
Supersedes https://github.com/facebook/react-native/pull/47296, which went stale.
Changelog:
[General][Breaking] - `AccessibilityState::selected` is now `std::optional<bool>` in C++ props, preserving an unset `selected` instead of coercing it to `false`
Reviewed By: javache
Differential Revision: D120049025
fbshipit-source-id: 53ac440976f00daffb3f0599c8841ada2a1452a6