Commit Graph
41806 Commits
Author SHA1 Message Date
Alan Hughes 212dae5631 Close the Android dev menu when the menu key is pressed again 2026-09-24 12:18:42 +01:00
Amro Altahtamouni b71d466561 Fix jest-preset resolution under pnpm and Yarn pnpm-mode (#58598)
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
2026-09-23 17:29:09 -07:00
Sam Zhou 18f5ddb6bb Upgrade flow in fbsource (#58655)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58655

X-link: https://github.com/react/metro/pull/1964

Changelog: [Internal]

Reviewed By: gkz

Differential Revision: D121416054

fbshipit-source-id: 451ecdfea111919e24bdbd274d00622bb66b50bc
2026-09-23 11:57:54 -07:00
Christian Falch b935e74ad5 Repair stale staged headers in the iOS prebuild (#58596)
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
2026-09-23 07:06:12 -07:00
Sedat Çiftçi 731ae45ae6 Reduce allocations in VirtualizedList render and scroll path (#58593)
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
2026-09-23 05:15:29 -07:00
Vitali Zaidman 7ce1b2c536 Format ParagraphEventEmitterTest.cpp with clang-format (#58649)
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
2026-09-23 04:49:01 -07:00
Jakub Piasecki 7a65eab410 Fix iOS frameworks build after switching to umbrella (#58646)
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
2026-09-23 04:09:08 -07:00
Nicola Corti d950a46cb9 Expand RNTester native interop Maestro coverage (#58444)
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
2026-09-23 03:08:07 -07:00
Nicola Corti b9a959643c Deep-link RNTester Pressable and Modal Maestro flows (#58482)
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
2026-09-23 03:08:07 -07:00
Nicola Corti eba89ccc99 Add deep-link Maestro flows for RNTester lists (#58481)
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
2026-09-23 03:08:07 -07:00
Nicola Corti b3839a3d03 Add deep-link Maestro flows for RNTester basics (#58480)
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
2026-09-23 03:08:07 -07:00
Sam Zhou 4b6cd413d6 Replace legacy compat types with their new names in TS based lib.dom.d.ts in xplat: 2/n (#58643)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58643

X-link: https://github.com/react/metro/pull/1960

Changelog: [Internal]

Reviewed By: gkz

Differential Revision: D121075922

fbshipit-source-id: 0f4bdcb339e2c3987dc5942deb47c026668a9fa1
2026-09-22 12:04:26 -07:00
Peter Abbondanzo 4ffc4a6523 Move sample TurboModule spec to RNTester (#58626)
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
2026-09-22 11:15:18 -07:00
Pieter De Baets a016535a83 Remove differentiator unflatten feature flag
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
2026-09-22 09:09:58 -07:00
Kaic Bastidas 099bcb444e Update eslint-plugin-ft-flow to fix ESLint v9 issue (#55628)
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
2026-09-22 08:17:41 -07:00
Dawid Małecki d381a99491 Use public umbrellas in text headers (#58571)
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
2026-09-22 08:04:18 -07:00
Christian Falch 97cc934dc7 Stop re-creating library package roots on every sync (#58597)
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
2026-09-22 06:57:35 -07:00
Jakub Piasecki 900b717e83 Update branching implementation to default to the JS thread merge (#58615)
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
2026-09-22 06:30:26 -07:00
Dawid Małecki b16845f69b Add umbrella for react/renderer/graphics subtree (#58373)
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
2026-09-22 06:26:57 -07:00
Dawid Małecki 335ef070e6 Fix framework umbrella search paths (#58622)
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
2026-09-22 05:55:32 -07:00
Pieter De Baets 17742436bd Use canonical Objective-C super initializer guards (#58629)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58629

Consolidate split superclass initializer assignments and validity checks into the canonical assignment-in-condition form. Configure `readability-implicit-bool-conversion` to allow pointer conditions so clang-tidy accepts that Objective-C idiom while retaining its other implicit-conversion coverage.

Changelog: [Internal]

Reviewed By: Abbondanzo

Differential Revision: D121046662

fbshipit-source-id: e50af970437047ca7c515e97cbcd4477188424eb
2026-09-22 04:49:42 -07:00
generatedunixname1563563004708334 b7c0e4986a xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp (#58633)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/58633

Reviewed By: javache

Differential Revision: D121155236

fbshipit-source-id: d38c8495e6e2aa0d35a23f4b830a94a9fd4a5561
2026-09-22 03:46:15 -07:00
React Native Bot baee2f3dd1 Add changelog for v0.88.0-rc.2 (#58627)
Summary:
Add Changelog for 0.88.0-rc.2

## Changelog:
[Internal] - Add Changelog for 0.88.0-rc.2

Pull Request resolved: https://github.com/react/react-native/pull/58627

Test Plan: N/A

Reviewed By: cipolleschi

Differential Revision: D121153607

Pulled By: fabriziocucci

fbshipit-source-id: 3afe299fd796c730b2f023c6006b9f3fa127ea2f
2026-09-22 02:07:50 -07:00
lukmccall f03f6c2b85 Align Kotlin version with the AGP Kotlin Gradle plugin (#58624)
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
2026-09-21 14:34:42 -07:00
Pieter De Baets 509e672f00 Remove init-variables clang-tidy workarounds (#58628)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58628

Restore idiomatic Objective-C fast enumeration now that React Native no longer enables the incompatible `cppcoreguidelines-init-variables` check.

Changelog: [Internal]

Reviewed By: Abbondanzo

Differential Revision: D121040575

fbshipit-source-id: 12313370081ee7563c4db02d0d4546bfad728610
2026-09-21 13:58:50 -07:00
Rubén Norte 3d93b5a1fa Remove disabled accessibility manager integration test (#58612)
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
2026-09-21 11:31:38 -07:00
Rubén Norte 9b021cee52 Remove remaining package deep imports (#58604)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58604

Migrate the remaining package consumers to React Native root exports, documented secondary entry points, global web APIs, or package-relative implementation paths. Use feature flags instead of directly invoking web API setup helpers.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D120717573

fbshipit-source-id: bf1bdb8d49340dd0040a46eb388850bd4b8139fc
2026-09-21 11:31:38 -07:00
Rubén Norte d6adb9f48a Remove React Native deep imports from Fantom (#58605)
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
2026-09-21 11:31:38 -07:00
Rubén Norte e0a6a9f465 Remove deep imports from generated Codegen modules (#58603)
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
2026-09-21 11:31:38 -07:00
Rubén Norte ee4fc1d7ea Remove deep imports from the Jest preset (#58602)
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
2026-09-21 11:31:38 -07:00
Rubén Norte 649b404a7e Expose renderer through unstable internals (#58618)
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
2026-09-21 11:31:38 -07:00
Nicola Corti 168a0d7b53 Allow configuring Babel runtime via caller in @react-native/babel-preset (#57974)
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
2026-09-21 10:10:01 -07:00
Sam Zhou 2761132459 Upgrade Flow in fbsource
Summary: Upgrade Flow.

Reviewed By: gkz

Differential Revision: D121017087

fbshipit-source-id: d5e9b717239db8c0369097d9d084c7450857f5d7
2026-09-21 09:49:37 -07:00
Jakub Piasecki f1d2756448 Introduce enableFabricCommitBranchingMergeOnMainThread feature flag (#58614)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58614

Changelog: [Internal]

Adds `enableFabricCommitBranchingMergeOnMainThread` feature flag for follow-up diffs.

Reviewed By: javache

Differential Revision: D120981615

fbshipit-source-id: 2294f6bee061eebc01702e0224c95d0e2c625705
2026-09-21 09:00:06 -07:00
Rubén Norte 17dec8b163 Test ResizeObserver structured clone errors (#58592)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58592

Add structured-clone coverage for `ResizeObserver`, `ResizeObserverEntry`, and `ResizeObserverSize` using real observer entries.

Changelog: [Internal]

Reviewed By: andrewdacenko

Differential Revision: D120691617

fbshipit-source-id: 974c250d16496b5c46029ffea8dfbcc4162b2914
2026-09-21 07:12:18 -07:00
Nicola Corti 3e6b2bf8cc Retry artifact uploads in CI (#58620)
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
2026-09-21 06:51:11 -07:00
Rubén Norte 31c3240bae Mock NativeAnimatedHelper in the Jest preset (#58613)
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
2026-09-21 06:32:04 -07:00
Dawid Małecki 9665168818 Make RendererCore conversions Apple warning-clean (#58591)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58591

Add safe fallback cases to RendererCore enum conversions so public umbrella consumers compile with Apple's -Wswitch-default policy.

Changelog:
[Internal]

___

Differential Revision: D120687424

fbshipit-source-id: cb7be5b960835229d484d51ebc1d55ee3f75a751
2026-09-21 05:43:54 -07:00
Dawid Małecki 17a472e5fb Use public umbrellas in modal headers (#58570)
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
2026-09-21 05:43:54 -07:00
Nicola Corti 5e6c552aec Fix RCTSurfacePresenter formatting (#58617)
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
2026-09-21 05:15:22 -07:00
Nicola Corti 9da1baccc8 Configure Swift formatting in CI (#58616)
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
2026-09-21 04:13:47 -07:00
Dawid Małecki 8de8e08db0 Reland Bridging umbrella in generated modules (#58558)
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
2026-09-21 02:52:40 -07:00
Janic Duplessis b439ef7c3f Process synchronous event beats in the frame that requested them (#58530)
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
2026-09-21 02:10:25 -07:00
Jakub Piasecki 68542c5cc8 Use umbrella instead of a direct include in reactperflogger module (#58532)
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
2026-09-21 01:55:38 -07:00
Jakub Piasecki bf892bf2a5 Correctly link umbrella headers for iOS prebuild (#58548)
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
2026-09-21 01:55:38 -07:00
Sam Zhou 434a306c73 Cleanup legacy library types in xplat: 1/n (#58609)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58609

Changelog: [Internal]

Differential Revision: D120838256

fbshipit-source-id: 1dd13e2748802bf50e6746257182fe1d0e4241ab
2026-09-20 10:52:30 -07:00
Jarren San Jose a7e422415f Remove root CLAUDE.md in favor of AGENTS.md (#58600)
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
2026-09-20 01:10:40 -07:00
Sam Zhou 6019ea14b2 Deploy 0.332.0 to xplat (#58608)
Summary:
X-link: https://github.com/react/metro/pull/1955

Pull Request resolved: https://github.com/react/react-native/pull/58608

[changelog](https://github.com/facebook/flow/blob/main/Changelog.md)
Changelog: [Internal]

Reviewed By: panagosg7

Differential Revision: D120845480

fbshipit-source-id: ccaa780e12d3ca033941de770f0ba31f90c7ad28
2026-09-19 14:49:02 -07:00
Paul Kim 15bb7e23f2 Include the native event timestamp in onHWKeyEvent on Android (#58607)
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
2026-09-19 11:46:13 -07:00
Calvin Liu af4d8eb08a Preserve unset accessibilityState.selected in C++ props (#58599)
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
2026-09-18 15:42:35 -07:00