Skip to content

pulling in latest React - #3

Open
balazsbajorics wants to merge 8129 commits into
concrete-utopia:masterfrom
react:main
Open

pulling in latest React#3
balazsbajorics wants to merge 8129 commits into
concrete-utopia:masterfrom
react:main

Conversation

@balazsbajorics

Copy link
Copy Markdown

No description provided.

Jack Pope and others added 30 commits March 19, 2026 14:51
Its now enabled everywhere other than keeping it dynamic in www
#35764)

## Summary

Enables Basic View Transition support for React Native Fabric renderer.

**Implemented:**
- Added FabricUIManager bindings for view transition methods:
`applyViewTransitionName`, `startViewTransition`
- Implemented `startViewTransition` with proper callback orchestration
(mutation → layout → afterMutation → spawnedWork → passive)
- Added fallback behavior that flushes work synchronously when Fabric's
`startViewTransition` returns null (e.g., when the ViewTransition
ReactNativeFeatureFlag is not enabled)
- Added Flow type declarations for new FabricUIManager methods
- Stubbed with `__DEV__` warnings for all the other view transition
config functions that are not yet implemented

This allows React Native apps using Fabric to leverage the View
Transition API for coordinated animations during state transitions, with
graceful degradation when the native side doesn't support it.

Below are diagrams of proposed architecture in fabric, and observation
of what/when config functions get called during a basic shared
transition example

<img width="2290" height="1529" alt="Untitled-2026-03-19-1240"
src="https://github.com/user-attachments/assets/192c9169-bc25-449c-a33b-dfec67179e7f"
/>

## How did you test this change?

- [x] `yarn flow fabric` - Flow type checks pass
- [x] `yarn lint` - Lint checks pass
- [x] Manually tested in Android catalyst app with
`enableViewTransition` and `enableViewTransitionForPersistenceMode `in
`ReactFeatureFlags.test-renderer.native-fb.js` and View Transition
enabled via ReactNativeFeatureFlag
- [x] Verified in the minified `ReactFabric-dev.fb.js` that the 'shim'
config functions are not included
- [x] Verified fallback behavior logs warning in `__DEV__` and flushes
work synchronously when ViewTransition flag isn't enabled in Fabric
The test is quite naive, but worth having as additional check for
changes in this logic.
…36094)

With this change, Components panel will display subtree of the Activity.
When it is in hidden mode, the subtree will be dimmed.

Added Jest tests and a sandbox case to `react-devtools-shell`.

Demo:


https://github.com/user-attachments/assets/69a2e8d6-585d-4fcd-b57e-e9ae06d0a1b3
Fixes #35821

Written/debugged by Claude.

## Test Plan

- Verify undoing the source fix fails the newly added test
- Verify building a bundle with the fix solves
https://github.com/gaearon/react-udv-bug/ repro
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
So in this PR the typo mistakes in the docs are corrected such as the 
1. **Ie** it should be **"i.e"**.
2. **errros** should be the **"errors"**.
3. **consdier** should be the **"consider"**.
4. **CreatFrom** should be **"CreateForm"**.


## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
I verified the fixes by reviewing the updated files locally to ensure
the corrected terms appear consistently and accurately in the
documentation.

---------

Co-authored-by: Yummy_Bacon5 <68166338+YummyBacon5@users.noreply.github.com>
We're currently hardcoding experimental options to
`eslint-plugin-react-hooks`. This blocks the release on features that
might not be ready.

This PR extends the ReactFeatureFlag infra to support flags for
`eslint-plugin-react-hooks`. An alternative would be to create a
separate flag system for build tools, but for now we have a small number
of these and reusing existing infra seems like the simplest approach.

I ran a full `yarn build` and checked the output resolved the flag
values as expected:


_build/oss-stable-semver/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js_
```js
var eprh_enableUseKeyedStateCompilerLint = false;
var eprh_enableVerboseNoSetStateInEffectCompilerLint = false;
var eprh_enableExhaustiveEffectDependenciesCompilerLint = 'off';
```


_build/facebook-www/ESLintPluginReactHooks-dev.classic.js_

```js
var eprh_enableUseKeyedStateCompilerLint = true;
var eprh_enableVerboseNoSetStateInEffectCompilerLint = true;
var eprh_enableExhaustiveEffectDependenciesCompilerLint = 'extra-only';
```

---------

Co-authored-by: lauren <lauren@anysphere.co>
Fixed spelling error in comment:
- Fixed 'accomodate' -> 'accommodate' in InferMutationAliasingEffects.ts

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
Fixed spelling errors:
- Fixed 'explicitlyu' -> 'explicitly' in compiler/CLAUDE.md
- Fixed 'intialized' -> 'initialized' in InferReactiveScopeVariables.ts
(comment)
- Fixed 'intialized' -> 'initialized' in InferMutationAliasingEffects.ts
(error message)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

I just fixed typos as followings.

- `succesful` → `successful`
- `becuase` → `because`
- `enought` → `enough`
- `defualt` → `default`

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->

This PR only includes test case description, dummy strings for test, and
comments updates, so it has no impact on runtime behavior.
Therefore, I manually reviewed changed texts to ensure correctness.
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary
This PR fixes a few small spelling errors in comments across the
codebase (`teh`→`the`, `occuring`→`occurring`, `occured`→`occurred`). No
behavior changes.

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
This is a comments-only change. I verified the diff is limited to
comment text and does not affect logic or runtime behavior.
## Summary

When a context value changes above a Suspense boundary that is showing
its fallback, context consumers inside the fallback do not re-render —
they display stale values.

`propagateContextChanges`, upon encountering a suspended Suspense
boundary, marks the boundary for retry but stops traversing into its
children entirely (`nextFiber = null`). This skips both the hidden
primary subtree (intentional — those fibers may not exist) and the
visible fallback subtree (a bug — those fibers are committed and visible
to the user).

The fix skips the primary OffscreenComponent and continues traversal
into the FallbackFragment, so fallback context consumers are found and
marked for re-render.

In practice this often goes unnoticed because it's uncommon to read
context inside a Suspense fallback, and when some other update (like a
prop change) flows into the fallback it sidesteps the propagation path
entirely. React Compiler makes the bug more likely to surface since it
memoizes more aggressively, reducing the chance of an incidental
re-render masking the stale value.

## Test plan

- Added regression test `'context change propagates to Suspense fallback
(memo boundary)'` in `ReactContextPropagation-test.js`
- Verified the test fails without the fix and passes with it
- All existing context propagation, Suspense, memo, and hooks tests pass
Compiler config parsing is currently done with new Function(...) which
is a XSS vulnerability. Replacing this with json parsing for safety
reasons.

Almost all compiler options (except for moduleTypeProvider) are json
compatible, so this isn't a big change to capabilities. Previously
created playground URLs with non-default configs may not be compatible
with this change, but we should be able to get the correct config
manually (by reading the JS version)
…6170)

Hi! While reviewing the React Compiler documentation, I noticed a few
minor issues in DESIGN_GOALS.md:


- Fixed a typo: `outweight` → `outweigh` in the Non-Goals section.

- Updated all instances of `ie` to the standard `i.e.` for better
consistency and clarity throughout the document.


Happy to contribute!

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Fixed a typo (outweight -> outweigh) and standardized abbreviation usage
(ie -> i.e.) in the DESIGN_GOALS.md file for the React Compiler
documentation. This improves the overall professionalism and readability
of the document.

## How did you test this change?

This is a documentation-only change. I verified the formatting and
consistency of the edits.
Fixed spelling errors in comments and error messages:
- Fixed 'occured' -> 'occurred' in ReactAsyncActions-test.js
- Fixed 'teh' -> 'the' in ReactFiberConfigDOM.js
- Fixed 'occured' -> 'occurred' in ErrorBoundary.js
- Fixed 'accomodate' -> 'accommodate' in InferMutationAliasingEffects.ts

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
This PR adds a benchmark fixture for measuring the performance overhead
of the React Server Components (RSC) Flight rendering compared to plain
Fizz server-side rendering.

### Motivation

Performance discussions around RSC (e.g. #36143, #35125) have
highlighted the need for reproducible benchmarks that accurately measure
the cost that Flight adds on top of Fizz. This fixture provides multiple
benchmark modes that can be used to track performance improvements
across commits, compare Node vs Edge (web streams) overhead, and
identify bottlenecks in Flight serialization and deserialization.

### What it measures

The benchmark renders a dashboard app with ~25 components (16 client
components), 200 product rows with nested data (~325KB Flight payload),
and ~250 Suspense boundaries in the async variant. It compares 8 render
variants: Fizz-only and Flight+Fizz, across Node and Edge stream APIs,
with both synchronous and asynchronous apps.

### Benchmark modes

- **`yarn bench`** runs a sequential in-process benchmark with realistic
Flight script injection (tee + `TransformStream`/`Transform` buffered
injection), matching what real frameworks do when inlining the RSC
payload into the HTML response for hydration.
- **`yarn bench:bare`** runs the same benchmark without script
injection, isolating the React-internal rendering cost. This is best for
tracking changes to Flight serialization or Fizz rendering.
- **`yarn bench:server`** starts an HTTP server and uses `autocannon` to
measure real req/s at `c=1` and `c=10`. The `c=1` results provide a
clean signal for tracking React-internal changes, while `c=10` reflects
throughput under concurrent load.
- **`yarn bench:concurrent`** runs an in-process concurrent benchmark
with 50 in-flight renders via `Promise.all`, measuring throughput
without HTTP overhead.
- **`yarn bench:profile`** collects CPU profiles via the V8 inspector
and reports the top functions by self-time along with GC pause data.
- **`yarn start`** starts the HTTP server for manual browser testing.
Appending `.rsc` to any Flight URL serves the raw Flight payload.

### Key findings during development

On Node 22, the Flight+Fizz overhead compared to Fizz-only rendering is
roughly:

- **Without script injection** (`bench:bare`): ~2.2x for sync, ~1.3x for
async
- **With script injection** (`bench:server`, c=1): ~2.9x for sync, ~1.8x
for async
- **Edge vs Node** adds another ~30% for sync and ~10% for async, driven
by the stream plumbing for script injection (tee + `TransformStream`
buffering)

The async variant better represents real-world applications where server
components fetch data asynchronously. Its lower overhead reflects the
fact that Flight serialization and Fizz rendering can overlap with I/O
wait times, making the added Flight cost a smaller fraction of total
request time.

The benchmark also revealed that the Edge vs Node gap is negligible for
Fizz-only rendering (~1-2%) but grows to ~15% for Flight+Fizz sync even
without script injection. With script injection (tee + `TransformStream`
buffering), the gap roughly doubles to ~30% for sync. The async variants
show smaller gaps (~5% without, ~10% with injection).
…default param (#36107)

## Summary

Fixes #36101

When a component function has a destructured prop with a `NewExpression`
default value (e.g. `{ value = new Number() }`), the React Compiler
bails out during HIR construction when trying to lower the default value
via `lowerReorderableExpression`. This causes
`validateNoSetStateInEffects` to never run, silently suppressing the
`set-state-in-effect` diagnostic.

**Root cause:** `isReorderableExpression` did not have a case for
`NewExpression`, so it fell through to the `default: return false`
branch. `lowerReorderableExpression` then recorded a `Todo` error and
aborted compilation of the function before any validation passes ran.

**Fix:** Add a `NewExpression` case to `isReorderableExpression` that
mirrors the existing `CallExpression` case — the expression is safe to
reorder when the callee and all arguments are themselves reorderable
(e.g. global identifiers and literals).

## How did you test this change?

Added a new compiler fixture
`invalid-setState-in-useEffect-new-expression-default-param` that
reproduces the bug from the issue. The fixture verifies that the
`EffectSetState` diagnostic is correctly emitted for a component with a
`NewExpression` default prop value.

All 1720 compiler snapshot tests pass.
We use FB_WWW bundle to inject internal feature flag values, but need to
use NODE guard type because this is a node script -- __DEV__ is breaking
internal builds

Follow up to #35951
…ld (#36243)

PR #35951 added FB_WWW_DEV builds for eslint-plugin-react-hooks to get
www-specific feature flag values. However, the FB_WWW build uses the
full ReactFeatureFlags.www.js fork, which contains:

  const dynamicFeatureFlags = require('ReactFeatureFlags');

This is a www Haste module that only exists in the www runtime. Rollup
can't tree-shake CJS require() calls (they're assumed side-effectful),
so the bare require('ReactFeatureFlags') survives in the build output
even though the eslint plugin only uses the static eprh_* exports.

When the built artifact is synced to www at
scripts/lint/eslint/rules/eslint-plugin-react-hooks/index.js, Node.js
fails with "Cannot find module 'ReactFeatureFlags'" because Haste
modules aren't available in the Node.js lint environment.

Create a dedicated fork (ReactFeatureFlags.eslint-plugin.www.js) that
exports only the static eprh_* flags with www values, without the
require('ReactFeatureFlags') dependency. Wire it up in forks.js for the
eslint-plugin-react-hooks entry point.

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

Co-authored-by: Eugene Choi <eugenechoi@meta.com>
…initeRenderLoopDetection (#36195)

My change in #35999 did not cover
all possible scenarios for emitting a warning, instead of throwing.

The instrumentation not only enables the identification for the infinite
loop via execution context checks, but also adds the check to more
lifecycle methods, like `markRootPinged` and `markRootUpdated`.

See the newly added test to understand a potential scenario. Before the
fix, the error would be thrown:
<img width="1192" height="424" alt="Screenshot 2026-04-08 at 17 21 51"
src="https://github.com/user-attachments/assets/ba8ea379-0271-4938-ae45-e37ee75e1963"
/>

With the current changes, the warning is logged with `console.error`.
## Summary

Set up the experiment to migrate event dispatching in the React Native
renderer to be based on the native EventTarget API.

Behind the `enableNativeEventTargetEventDispatching` flag, events are
dispatched through `dispatchTrustedEvent` instead of the legacy plugin
system.

Regular event handler props are NOT registered via addEventListener at
commit time. Instead, a hook on EventTarget
(`EVENT_TARGET_GET_DECLARATIVE_LISTENER_KEY`) extracts handlers from
`canonical.currentProps` at dispatch time, shifting cost from every
render to only when events fire. The hook is overridden in
ReactNativeElement to look up the prop name via a reverse mapping from
event names (built lazily from the view config registry).

Responder events bypass EventTarget entirely. `negotiateResponder` walks
the fiber tree directly (capture then bubble phase), calling handlers
from `canonical.currentProps` and checking return values inline.
Lifecycle events (`responderGrant`, `responderMove`, etc.) call handlers
directly from props and inspect return values — `onResponderGrant`
returning `true` blocks native responder,
`onResponderTerminationRequest` returning `false` refuses termination.
This eliminates all commit-time cost for responder events (no wrappers,
no addEventListener, no `responderWrappers` on canonical).

## How did you test this change?

Flow
Tested e2e in RN using Fantom tests (that will land after this).
## Summary

We found a bug in the logic in
#36253 and we realized it's very
inconvenient to iterate on the implementation when it's in this
repository, as we're forced to then synchronize it to RN to test
changes.

This moves the entire implementation to RN for simplicity and also to
simplify some clean ups in the future (like removing `top` prefixes from
native event types).

## How did you test this change?

The changes are gated. Will test e2e in RN.
## Summary

The Paper renderer is no longer used in React Native. This commit
removes all remaining Paper source code, tests, build system references,
and Paper backward-compatibility branches in shared code.

Deleted Paper-only source files:
- ReactNativeRenderer.js, ReactNativeInjection.js,
ReactFiberConfigNative.js
- ReactNativeComponentTree.js, ReactNativeEventEmitter.js
- ReactNativeFiberHostComponent.js, ReactNativeGlobalResponderHandler.js
- ReactNativeAttributePayload.js, NativeMethodsMixinUtils.js
- ReactFiberConfig.native.js (reconciler fork)
- index.js (Paper entry point)

Cleaned up shared files:
- ReactNativePublicCompat.js: removed _nativeTag checks, UIManager/
legacySendAccessibilityEvent Paper fallbacks
- ReactNativeFiberInspector.js: removed getInspectorDataForViewTag,
UIManager.measure fallback, Paper branch in
getInspectorDataForViewAtPoint
- ReactFiberConfigFabric.js: removed _nativeTag backward compat in
getPublicInstance, removed getInspectorDataForViewTag from devtools
config
- ReactNativeTypes.js: removed ReactNativeType (Paper API type)

Cleaned up build system:
- inlinedHostConfigs.js: removed shortName 'native' config
- forks.js: removed dead 'react-native-renderer' case
- Deleted ReactNative.js shim and Paper-only test mocks

## How did you test this change?

Manually synced the renderer to RN and passed all Fantom tests.

Manually verified the differences in the generated `ReactFabric-dev.js`
file. Only Paper compat logic has been removed.

<details>
<summary>diff</summary> 

```diff
--- /tmp/react-fabric-baseline/ReactFabric-dev.js	2026-04-16 16:42:42
+++ build/react-native/implementations/ReactFabric-dev.js	2026-04-16 18:08:43
@@ -30,43 +30,19 @@
         : emptyObject;
     }
     function createHierarchy(fiberHierarchy) {
-      return fiberHierarchy.map(function (fiber$jscomp$0) {
+      return fiberHierarchy.map(function (fiber) {
         return {
-          name: getComponentNameFromType(fiber$jscomp$0.type),
+          name: getComponentNameFromType(fiber.type),
           getInspectorData: function () {
             return {
-              props: getHostProps(fiber$jscomp$0),
+              props: getHostProps(fiber),
               measure: function (callback) {
-                var hostFiber = findCurrentHostFiber(fiber$jscomp$0);
-                if (
-                  (hostFiber =
-                    null != hostFiber &&
-                    null !== hostFiber.stateNode &&
-                    hostFiber.stateNode.node)
-                )
+                var hostFiber = findCurrentHostFiber(fiber);
+                (hostFiber =
+                  null != hostFiber &&
+                  null !== hostFiber.stateNode &&
+                  hostFiber.stateNode.node) &&
                   nativeFabricUIManager.measure(hostFiber, callback);
-                else {
-                  hostFiber = ReactNativePrivateInterface.UIManager;
-                  var JSCompiler_temp_const = hostFiber.measure,
-                    JSCompiler_inline_result;
-                  a: {
-                    for (var fiber = fiber$jscomp$0; fiber; ) {
-                      null !== fiber.stateNode &&
-                        5 === fiber.tag &&
-                        (JSCompiler_inline_result = findNodeHandle(
-                          fiber.stateNode
-                        ));
-                      if (JSCompiler_inline_result) break a;
-                      fiber = fiber.child;
-                    }
-                    JSCompiler_inline_result = null;
-                  }
-                  return JSCompiler_temp_const.call(
-                    hostFiber,
-                    JSCompiler_inline_result,
-                    callback
-                  );
-                }
               }
             };
           }
@@ -1805,18 +1781,6 @@
       }
       return null;
     }
-    function doesFiberContain(parentFiber, childFiber) {
-      for (
-        var parentFiberAlternate = parentFiber.alternate;
-        null !== childFiber;
-
-      ) {
-        if (childFiber === parentFiber || childFiber === parentFiberAlternate)
-          return !0;
-        childFiber = childFiber.return;
-      }
-      return !1;
-    }
     function traverseVisibleHostChildren(
       child,
       searchWithinHosts,
@@ -16986,44 +16950,6 @@
     function getCurrentFiberForDevTools() {
       return current;
     }
-    function findNodeHandle(componentOrHandle) {
-      var owner = current;
-      null !== owner &&
-        isRendering &&
-        null !== owner.stateNode &&
-        (owner.stateNode._warnedAboutRefsInRender ||
-          console.error(
-            "%s is accessing findNodeHandle inside its render(). render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.",
-            getComponentNameFromType(owner.type) || "A component"
-          ),
-        (owner.stateNode._warnedAboutRefsInRender = !0));
-      if (null == componentOrHandle) return null;
-      if ("number" === typeof componentOrHandle) return componentOrHandle;
-      if (componentOrHandle._nativeTag) return componentOrHandle._nativeTag;
-      if (
-        null != componentOrHandle.canonical &&
-        null != componentOrHandle.canonical.nativeTag
-      )
-        return componentOrHandle.canonical.nativeTag;
-      if (
-        (owner =
-          ReactNativePrivateInterface.getNativeTagFromPublicInstance(
-            componentOrHandle
-          ))
-      )
-        return owner;
-      componentOrHandle = findHostInstanceWithWarning(
-        componentOrHandle,
-        "findNodeHandle"
-      );
-      return null == componentOrHandle
-        ? componentOrHandle
-        : null != componentOrHandle._nativeTag
-          ? componentOrHandle._nativeTag
-          : ReactNativePrivateInterface.getNativeTagFromPublicInstance(
-              componentOrHandle
-            );
-    }
     function getNodeFromInternalInstanceHandle(internalInstanceHandle) {
       return (
         internalInstanceHandle &&
@@ -17134,12 +17060,9 @@
         }
         return instance.canonical.publicInstance;
       }
-      return null != instance.containerInfo &&
-        null != instance.containerInfo.publicInstance
+      return null != instance.containerInfo
         ? instance.containerInfo.publicInstance
-        : null != instance._nativeTag
-          ? instance
-          : null;
+        : null;
     }
     function getPublicInstanceFromHostFiber(fiber) {
       fiber = getPublicInstance(fiber.stateNode);
@@ -18017,7 +17940,6 @@
       DefaultEventPriority = 32,
       IdleEventPriority = 268435456,
       searchTarget = null,
-      instanceCache = new Map(),
       bind = Function.prototype.bind,
       valueStack = [];
     var fiberStack = [];
@@ -20041,24 +19963,19 @@
         _nativeFabricUIManage.unstable_getCurrentEventPriority,
       extraDevToolsConfig = {
         getInspectorDataForInstance: getInspectorDataForInstance,
-        getInspectorDataForViewTag: function (viewTag) {
-          viewTag = instanceCache.get(viewTag) || null;
-          return getInspectorDataForInstance(viewTag);
-        },
         getInspectorDataForViewAtPoint: function (
           inspectedView,
           locationX,
           locationY,
           callback
         ) {
-          var closestInstance = null,
-            fabricNode =
-              ReactNativePrivateInterface.getNodeFromPublicInstance(
-                inspectedView
-              );
-          fabricNode
+          var closestInstance = null;
+          (inspectedView =
+            ReactNativePrivateInterface.getNodeFromPublicInstance(
+              inspectedView
+            ))
             ? nativeFabricUIManager.findNodeAtPoint(
-                fabricNode,
+                inspectedView,
                 locationX,
                 locationY,
                 function (internalInstanceHandle) {
@@ -20109,32 +20026,9 @@
                   }
                 }
               )
-            : null != inspectedView._internalFiberInstanceHandleDEV
-              ? ReactNativePrivateInterface.UIManager.findSubviewIn(
-                  findNodeHandle(inspectedView),
-                  [locationX, locationY],
-                  function (nativeViewTag, left, top, width, height) {
-                    var inspectorData = getInspectorDataForInstance(
-                      instanceCache.get(nativeViewTag) || null
-                    );
-                    callback(
-                      assign({}, inspectorData, {
-                        pointerY: locationY,
-                        frame: {
-                          left: left,
-                          top: top,
-                          width: width,
-                          height: height
-                        },
-                        touchedViewTag: nativeViewTag,
-                        closestPublicInstance: nativeViewTag
-                      })
-                    );
-                  }
-                )
-              : console.error(
-                  "getInspectorDataForViewAtPoint expects to receive a host component"
-                );
+            : console.error(
+                "getInspectorDataForViewAtPoint expects to receive a host component"
+              );
         }
       },
       getViewConfigForType =
@@ -20368,23 +20262,12 @@
       );
     };
     exports.dispatchCommand = function (handle, command, args) {
-      var nativeTag =
-        null != handle._nativeTag
-          ? handle._nativeTag
-          : ReactNativePrivateInterface.getNativeTagFromPublicInstance(handle);
-      null == nativeTag
-        ? console.error(
+      handle = ReactNativePrivateInterface.getNodeFromPublicInstance(handle);
+      null != handle
+        ? nativeFabricUIManager.dispatchCommand(handle, command, args)
+        : console.error(
             "dispatchCommand was called with a ref that isn't a native component. Use React.forwardRef to get access to the underlying native component"
-          )
-        : ((handle =
-            ReactNativePrivateInterface.getNodeFromPublicInstance(handle)),
-          null != handle
-            ? nativeFabricUIManager.dispatchCommand(handle, command, args)
-            : ReactNativePrivateInterface.UIManager.dispatchViewManagerCommand(
-                nativeTag,
-                command,
-                args
-              ));
+          );
     };
     exports.findHostInstance_DEPRECATED = function (componentOrHandle) {
       var owner = current;
@@ -20402,14 +20285,46 @@
         : componentOrHandle.canonical &&
             componentOrHandle.canonical.publicInstance
           ? componentOrHandle.canonical.publicInstance
-          : componentOrHandle._nativeTag
-            ? componentOrHandle
-            : findHostInstanceWithWarning(
-                componentOrHandle,
-                "findHostInstance_DEPRECATED"
-              );
+          : findHostInstanceWithWarning(
+              componentOrHandle,
+              "findHostInstance_DEPRECATED"
+            );
     };
-    exports.findNodeHandle = findNodeHandle;
+    exports.findNodeHandle = function (componentOrHandle) {
+      var owner = current;
+      null !== owner &&
+        isRendering &&
+        null !== owner.stateNode &&
+        (owner.stateNode._warnedAboutRefsInRender ||
+          console.error(
+            "%s is accessing findNodeHandle inside its render(). render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.",
+            getComponentNameFromType(owner.type) || "A component"
+          ),
+        (owner.stateNode._warnedAboutRefsInRender = !0));
+      if (null == componentOrHandle) return null;
+      if ("number" === typeof componentOrHandle) return componentOrHandle;
+      if (
+        null != componentOrHandle.canonical &&
+        null != componentOrHandle.canonical.nativeTag
+      )
+        return componentOrHandle.canonical.nativeTag;
+      if (
+        (owner =
+          ReactNativePrivateInterface.getNativeTagFromPublicInstance(
+            componentOrHandle
+          ))
+      )
+        return owner;
+      componentOrHandle = findHostInstanceWithWarning(
+        componentOrHandle,
+        "findNodeHandle"
+      );
+      return null == componentOrHandle
+        ? componentOrHandle
+        : ReactNativePrivateInterface.getNativeTagFromPublicInstance(
+            componentOrHandle
+          );
+    };
     exports.getNodeFromInternalInstanceHandle =
       getNodeFromInternalInstanceHandle;
     exports.getPublicInstanceFromInternalInstanceHandle = function (
@@ -20433,14 +20348,6 @@
         : null;
     };
     exports.isChildPublicInstance = function (parentInstance, childInstance) {
-      if (
-        parentInstance._internalFiberInstanceHandleDEV &&
-        childInstance._internalFiberInstanceHandleDEV
-      )
-        return doesFiberContain(
-          parentInstance._internalFiberInstanceHandleDEV,
-          childInstance._internalFiberInstanceHandleDEV
-        );
       parentInstance =
         ReactNativePrivateInterface.getInternalInstanceHandleFromPublicInstance(
           parentInstance
@@ -20449,9 +20356,27 @@
         ReactNativePrivateInterface.getInternalInstanceHandleFromPublicInstance(
           childInstance
         );
-      return null != parentInstance && null != childInstance
-        ? doesFiberContain(parentInstance, childInstance)
-        : !1;
+      if (null != parentInstance && null != childInstance) {
+        a: {
+          for (
+            var parentFiberAlternate = parentInstance.alternate;
+            null !== childInstance;
+
+          ) {
+            if (
+              childInstance === parentInstance ||
+              childInstance === parentFiberAlternate
+            ) {
+              parentInstance = !0;
+              break a;
+            }
+            childInstance = childInstance.return;
+          }
+          parentInstance = !1;
+        }
+        return parentInstance;
+      }
+      return !1;
     };
     exports.render = function (
       element,
@@ -20521,22 +20446,12 @@
       return element;
     };
     exports.sendAccessibilityEvent = function (handle, eventType) {
-      var nativeTag =
-        null != handle._nativeTag
-          ? handle._nativeTag
-          : ReactNativePrivateInterface.getNativeTagFromPublicInstance(handle);
-      null == nativeTag
-        ? console.error(
+      handle = ReactNativePrivateInterface.getNodeFromPublicInstance(handle);
+      null != handle
+        ? nativeFabricUIManager.sendAccessibilityEvent(handle, eventType)
+        : console.error(
             "sendAccessibilityEvent was called with a ref that isn't a native component. Use React.forwardRef to get access to the underlying native component"
-          )
-        : ((handle =
-            ReactNativePrivateInterface.getNodeFromPublicInstance(handle)),
-          null != handle
-            ? nativeFabricUIManager.sendAccessibilityEvent(handle, eventType)
-            : ReactNativePrivateInterface.legacySendAccessibilityEvent(
-                nativeTag,
-                eventType
-              ));
+          );
     };
     exports.stopSurface = function (containerTag) {
       var root = roots.get(containerTag);
```

</details>
…le times (#36287)

It is possible for the fallback tasks from a Suspense boundary to
trigger an early `completeAll` call which is later repeated due to
`finishedTask` reentrancy. For node.js in particular this might be
problematic since we invoke a callback on each `completeAll` call but in
general it just isn't the right semantics since the call is running
slightly earlier than the completion of the last `finishedTask`
invocation. This change ensures that any reentrant `finishedTask` calls
(due to soft aborting fallback tasks) omit the `completeAll` call by
temporarily incrementing the total pending tasks.
## Summary
- Imports `startViewTransitionReadyFinished` from
`nativeFabricUIManager` in `ReactFiberConfigFabricWithViewTransition`
- Calls `fabricStartViewTransitionReadyFinished()` when the view
transition `ready` promise resolves

This is not a config function, but it's helpful to have it notify fabric
ViewTransition runtime when ready callback is done. Right now we're
testing animation kicked off from view transition event handlers, this
is signal to know when animations that belong to a transition have all
started.

## Test plan
- Existing Fabric renderer tests should continue to pass
- View transition ready callback now notifies the native module when
finished
…n in Fabric (#36196)

## Summary
- Wires up the native `fabricCreateViewTransitionInstance` call in
`createViewTransitionInstance` which will create a ShadowNode for old
pseudo element
- Extracts tag allocation logic into a shared `allocateTag()` function
exported from `ReactFiberConfigFabric`
  - Imports `allocateTag` in `ReactFiberConfigFabricWithViewTransition`
- Reuses `allocateTag()` in `createInstance` and `createTextInstance`
instead of inline tag incrementing
- Wires up native `fabricSuspendOnActiveViewTransition` call in
`suspendOnActiveViewTransition` which suspends another view transition
when the previous one is not yet finished

## Test plan
- Existing Fabric renderer tests should continue to pass
- ViewTransition instance creation now properly allocates a tag and
calls the native module
Hendrik Liebau and others added 30 commits August 24, 2026 19:46
#37357)

This follows #37315, which added the render lifetime controller to bound
the abort listener that `attachAbortSignal` attaches to a caller's
signal. `RequestInstance` constructed one for every request, so a render
that is given no signal allocated a controller, aborted it on
completion, and nothing ever observed either.

The controller is now created in `attachAbortSignal`, and the three
places that end the lifetime go through `endRenderLifetime`, which does
nothing when there is no controller. Callers that pass a signal are
unaffected. Callers that do not no longer allocate one, and `signal` is
optional in every browser, edge and static entry point, while
`renderToPipeableStream` and `resumeToPipeableStream` accept no signal
at all.

They also no longer reach `AbortController` at all, which matters more
than the allocation. Fizz had no runtime dependency on it before #37315,
and an unconditional one reaches environments that provide the API
through a polyfill. An incomplete polyfill can then fail a render that
never asked for abort support.

The new test asserts that no controller is constructed when no signal is
passed. It fails with the eager construction restored, since nothing
else in the suite would notice a regression to it.
## Summary

Propagate errors returned while lowering block statements instead of
discarding them and continuing with partially built HIR.

The original issue was triggered by the SWC path, where a TypeScript
`this` pseudo-parameter remains in the AST but is omitted from
`ScopeInfo`. Lowering rejects the AST parameter, but the function-body
block wrapper previously swallowed that error and returned a partial
function.

```ts
function Component() {
  useEffect(() => {
    const get = (): Val => {
      window.value = {
        count: 0,
        method(this: Val) {},
      };
      return window.value;
    };
    get().count++;
  }, []);
}
```

This could emit a partial transform with the assignment removed:

```js
function Component() {
  useEffect(() => {
    const get = () => {
      return window.value;
    };
    get().count++;
  }, []);
}
```

## How did you test this change?

Added a [source-level reproduction against the SWC
adapter](https://github.com/wbinnssmith/swc/blob/114e9c55b2/crates/swc_ecma_react_compiler/src/tests/integration.rs#L1563-L1591)
using the same case above.

- With the current lowering crate, the test fails because the adapter
emits a partial program.
- With this change patched into the lowering dependency, the test passes
because compilation bails out.
- `cargo test --manifest-path compiler/Cargo.toml -p
react_compiler_lowering`
)

Every downstream job in `runtime_build_and_test.yml` restored the 50
`_build_*` artifacts with `actions/download-artifact` only after
setup-node, the node_modules cache restore, and any installs had
completed, even though the download is independent of all of them.

This change marks the download as a [background
step](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstepsbackground)
started immediately after checkout, and adds an explicit `wait:
download_build` before the first step that reads `build/`.

The download starts after checkout because `actions/checkout` runs `git
clean`, which would wipe a previously downloaded `build/` directory. The
`sizebot` job is unchanged because its base-build download also writes
`./build` and would collide with a concurrent artifact restore.

This only shaves of a few seconds from wall time. It's more about
establishing precedent.

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
In `build_and_lint`, `actions/setup-java` ran sequentially between
setup-node and the node_modules cache restore, but Java is only needed
by `yarn build` for the Closure Compiler bundles. This change marks the
setup-java step as a background step.

Setting up Java is mostly network (download) and CPU (unpack). It
overlaps with installing/restoring node_modules which is network and FS
work. So we aren't competing for resources that would make concurrently
running steps moot.

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
…ical (#37373)

The build workers can restore different weights if they don't restore
the cache at the exact same time. The more time difference, the more
likely they restore different weights which could lead to some bundles
not being built at all (e.g.
https://github.com/react/react/actions/runs/32822851267).



A new job now restores the latest entry once per run and republishes it
as a per-run artifact. The new job sits adds no wall time because it
runs in parallel with `runtime_compiler_node_modules_cache`, which
already gates the build workers and takes about 30 seconds on a cache
hit, while the resolve job does strictly less work (no checkout, no Node
setup, a 5KB cache entry instead of the node_modules restore), so it
finishes first and the build workers start at the same time as before.

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
…37379)

Test files running in the Node.js Jest environment share the worker
process's `performance` object, because [`jest-environment-node`
installs it by
reference](https://github.com/jestjs/jest/blob/v29.7.0/packages/jest-environment-node/src/index.ts#L85-L103)
rather than by copy. When a test mocked the clock with
`Object.defineProperty(performance, 'now', ...)`, the mutation hit the
shared object and was never undone, since Jest only restores
`jest.spyOn` mocks when a file's runtime is torn down ([`jest-runtime`'s
`teardown()` calls
`restoreAllMocks()`](https://github.com/jestjs/jest/blob/v29.7.0/packages/jest-runtime/src/index.ts#L1358-L1359)).
Every subsequent test file in the same worker then observed the fake
clock, including jsdom-based files, whose [`performance.now()` subtracts
a window-creation timestamp from the shared object's
`now()`](https://github.com/jsdom/jsdom/blob/v22.1.0/lib/jsdom/living/hr-time/Performance-impl.js#L13-L14).

This change switches the six affected test files to
`jest.spyOn(performance, 'now')` and `jest.spyOn(performance,
'timeOrigin', 'get')`, which Jest restores automatically at teardown.
`ReactFlightDOMEdge-test.js` runs in jsdom and therefore did not leak,
but it used the same pattern and is converted for consistency.

This change is mostly for test hygiene. Was discovered while
investigating a flaky `{"time":NaN}` serialisation bug (e.g.
https://github.com/react/react/actions/runs/32878999825/job/97903912119)

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
… false` (#37251)

`FragmentInstance` tracks its event listeners so they can be applied to
children added later, and matches them by a normalized options identity.
Omitted options currently normalize to a different identity than an
explicit `false` or `{capture: false}`, even though both mean `capture:
false` per the `EventTarget` contract, where listener identity is the
tuple of type, callback, and capture flag. As a result, a listener added
without an options argument cannot be removed with an explicit
capture-false value (or the reverse).

This change normalizes omitted options to the same capture-false
identity as `false` and `{capture: false}`. The first commit adds a test
to the FragmentRef suite characterizing the current behavior; the second
commit contains the fix and the updated assertions.

---------

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
…ges (#37384)

The custom `toThrow` override in `scripts/jest/matchers/toThrow.js`
wrapped the built-in matcher to rewrite the pre-Node-17 V8 error message
format ("Cannot read property 'x' of undefined") into the modern one
("Cannot read properties of undefined (reading 'x')"), so the test suite
could run on Node 12 to 16.

On the Node versions this repo runs on (20 per `.nvmrc`, 24 in CI), V8
only ever produces the modern format, so the override is a passthrough.

Mostly removing this because the custom matcher deep-imports
`expect/build/toThrowMatchers`, which no longer resolves on Jest 30
because each Jest package is now bundled into a single file, so this
removal unblocks the Jest 30 upgrade stacked on top.

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
`ToggleEvent` carries a `source` property pointing at the control that
opened or closed a popover, but `ToggleEventInterface` only lists
`newState` and `oldState`, so the synthetic event never copies it. An
`onToggle` handler reads `event.source` as `undefined` even when the
native event has it.

Adding `source` to the interface copies it off the native event the same
way `newState` and `oldState` are copied.
Bumps Jest to latest 30.x

The `resolutions` pin that kept jsdom at 22.1.0 is removed, so the test
environment now runs the jsdom version that jest-environment-jsdom
declares (26.1.0 on Jest 30).

The matcher aliases that Jest 30 deleted are replaced with their
canonical forms across the test suites: `toBeCalled`, `toBeCalledTimes`,
`toBeCalledWith`, and `lastCalledWith` become the corresponding
`toHaveBeenCalled*` matchers, and `toThrowError` becomes `toThrow`. The
custom `toThrow` matcher override is removed in the PR below this one.

Jest 30 activates the `node` export condition for CommonJS requires in
every test environment, so in the jsdom-based Flight suites
`react-server-dom-webpack/client` now resolves to the Node build (which
requires an options argument) instead of the browser build. Those suites
now map the client entry to `client.browser` explicitly, matching the
existing mocks for the `server` and `static` entries, and the Turbopack
Node test uses `jest.requireActual` because `client` and `client.node`
now resolve to the same file, which otherwise made the mock factory
recurse. For the same reason `react-dom/static` resolves to the
lazily-initialized Node entry in source mode, so its version-mismatch
test is gated to build mode like the other server-entry tests.

The obsolete `prettierPath` override is dropped from the base Jest
config because Jest 30 works with Prettier 3 for inline snapshots, which
also fixes `yarn test -u` crashing on the repo's Prettier 3-only hermes
plugin, and the now unused `prettier-2` alias dependency is removed with
it. Snapshot files are regenerated for Jest 30's updated snapshot header
and formatting. One test now passes a number instead of a string to
`jest.advanceTimersByTime`, which fake-timers v13 no longer coerces.

---------

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
Co-authored-by: Sebastian "Sebbie" Silbermann <sebastian.silbermann@vercel.com>
)

Fragment IntersectionObserver targets used to stay observed after a
child was removed so the exit record (isIntersecting: false) could still
fire, but that left the observer holding detached nodes. We now
unobserve ResizeObserver targets immediately, and delay
IntersectionObserver unobserve until after paint so the exit still
lands, then drop the strong ref.

If the same node is reinserted before that flush, we cancel the pending
unobserve so a later cleanup does not detach a child that’s still
visible.

Closes #37452
Closes #37302
Fixes #37451
Listeners registered with `options.signal` are supposed to be removed
when the signal is aborted.

Since `FragmentInstance` does not clean up its tracked listeners on
abort, previously removed listeners cannot be re-attached.
## Summary

- configure the React Compiler Rust crates as a Cargo workspace with
shared package metadata and versioned internal dependencies
- add workflows to open a signed version-bump pull request and publish
the workspace through crates.io trusted publishing
- add release scripts and contributor documentation for versioning,
validation, and publishing

## Test plan

- Run `cargo check --locked --workspace` from `compiler/`.
- Run **(Compiler) Publish Rust Crates** from the Actions tab with **Dry
run** enabled; confirm every workspace crate packages successfully
without publishing.
- Run **(Compiler) Update Rust Crate Version** with a test version;
confirm it verifies one shared version and opens a signed version-bump
pull request containing the updated workspace manifest and lockfile.
## Summary

Alternative to #37280 that keeps the child-set assertion and instead
fixes the root cause.

The assertion "The children should not have changed if we pass in the
same set." fired while DevTools reconciled the hidden content tree of a
Suspense boundary that had just switched to its fallback.

`updateSuspenseChildrenRecursively` reconciles the content and the
fallback in two passes, but the previous-set lockstep pointer of the
content pass is not bounded. When a boundary is suspended on both sides
of a commit, the pointer advances from the previous content Offscreen
onto the previous fallback fragment, and the leftover-children check
reports `ShouldResetChildren` even though the fallback is reconciled in
the second pass by design.

For a boundary that is filtered from the tree, that flag propagates to
the parent child list, freezes its lockstep pointer, forces the
following sibling to be paired by alternate, and the instance scan
(which only matches the paired previous fiber) no longer finds the
existing instance, since instances track the current fiber. The subtree
below is then walked without its instance, which cascades into spurious
unmount and remount work and surfaces at the assertion in the filtered
same-child-set branch.

We're also avoiding creation of new backend instances in those
scenarios.

This change bounds the previous set of the content pass by the previous
fallback fragment via a new `prevLastChild` parameter, so the flag
disappears.

Closes #37280

## How did you test this change?

- cherry-picked test from #37280

---------

Co-authored-by: Ruslan Lesiutin <hoxy@meta.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
97.6% of the value sets tracked per identifier in mutation / aliasing
inference hold exactly one element, but each was a `FxHashSet`, meaning
each was a heap allocation.
Because the inference code retains a full state in each basic block,
these single-element hashsets were a major contributor to peak memory
allocation.

This replaces them with a small inline set inspired by smolvec /
tinyvec. Five values are stored inline, and any more spill to the heap.
This was only needed in **0.02%** of sets in my data corpus.

This also makes iteration order match the TS implementation. TS uses
`Set` and iterates in insertion order; the Fx set iterated in hash
order. This brings the two behaviors in line.

| Benchmark | Peak allocation | Allocation count | Wall time |

|------------------|-----------------------------|------------------|-----------|
| legacy/image.tsx | 33.40 -> 28.07 MiB (-16.0%) | -66.7% | -28.8% |
| next-client | 33.40 -> 28.07 (-16.0%) | -42.0% | -15.7% |
| devtools | 16.29 -> 14.25 (-12.5%) | -19.9% | -7.3% |
| fixtures | 9.41 -> 7.90 (-16.0%) | -7.3% | -3.3% |
| next-examples | 4.85 -> 4.85 ( 0.0%) | -4.8% | -1.6% |
…ning (#37376)

tl;dr reduces memory churn by 7.8%, allocations by 4.1%, wall time by
~4%

## Summary

When compiled functions are written back to the AST,
`apply_compiled_functions` took a reference to a slice, and deep-cloned
each compiled body out of it. In theTS version this step assigns
references through Babel paths, which are essentially free. The Rust
port translated that as `.clone()` for safety, deep-copying the entire
codegen output for every compiled function.

Nothing needs these bodies after they are inserted, and the caller
already owns the vector. So this takes `compiled_fns` by value and moves
the data into the AST instead:

* `ReplaceFnVisitor` holds an `Option<CodegenFunction>` and moves it to
whatever arm matches
* Outlined function declarations move their id+params+body out of
`codegen_fn.outlined` rather than cloning them
* `needs_memo_import` is computed before the loop that consumes the
vector. Only the computation moved; the block that registers the import
stays where it was, so ordering and identifier numbering don't change.

This doesn't fully eliminate clones, just ones where it's easy to do a
move instead.

## How did you test this change?

All compiler fixtures pass with byte-identical output
Sizebot compared the pull request head build against the build of
`pull_request.base.sha`, which is the tip of the base branch at event
time, not the commit the pull request diverged from. The field's
semantics are undocumented in GitHub's API schema (the OpenAPI
description types it as a bare string); the observed behavior and the
compare API's `merge_base_commit` confirm the difference.

The difference between `pull_request.base.sha` and the merge base is
confirmed with an example in #37356

The sizebot job now resolves the merge-base through the compare API and
downloads the base build for that commit instead, so the report only
ever contains the pull request's own changes. The job gains `contents:
read` for the compare call.

When no base build can be downloaded for the merge-base, for example
because its artifacts aged out of the retention window or its run
failed, the sizebot job records a `base-build-not-found` result instead
of failing immediately. `render-comment.js` on the default branch
renders that as a warning comment naming the base commit and writes the
`sizebot-problem.txt` marker, so the comment workflow fails its check
after posting the warning, the same pattern already used for build
configuration drift. The sizebot job itself intentionally stays green: a
failed run would make the renderer discard the results and mask the
warning with a generic "did not complete" message.

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
## Summary

`git show --format=%h` is not stable: abbreviation length depends on
`core.abbrev` and how unique the prefix is in that clone. Two rebuilds
of the same commit can therefore embed different strings in
`DEVTOOLS_VERSION` and the extension manifest.

Always take the full hash (`%H`) and slice it to 10 characters so the
value is the same everywhere.

This also removes the `build/COMMIT_SHA` fallback used when Mozilla
rebuilds from a git archive (no `.git`). That path used a different
length (7) and a different source, so it could not match a git checkout
of the same commit. Firefox source review should rebuild from a checkout
of the commit in #37307, not from the tarball alone.

Stack: this PR → #37306#37307.

## How did you test this change?

Build-script only. `getGitCommit()` now returns `HEAD` sliced to 10
chars, independent of `core.abbrev`.

Co-authored-by: Ruslan Lesiutin <hoxy@meta.com>
## Summary

The extension build writes `new Date().toLocaleDateString()` into
Chrome/Edge `version_name` and into every browser's manifest
description. That string changes with the calendar, timezone, and
locale, so a Firefox AMO rebuild on another day cannot match the
uploaded zip.

Stop stamping dates. `version_name` stays the value from the source
manifest (still updated by `prepare-release.js` on version bumps). The
description still records the commit from #37305.

Depends on #37305. Next: #37307.

## How did you test this change?

Build-script only. After this, `manifest.json` description is `Created
from revision <commit>.` and Chrome/Edge `version_name` is the committed
version string.

---------

Co-authored-by: Ruslan Lesiutin <hoxy@meta.com>
## Summary

`build-and-test.js` could feed three different commits into one release:
`git archive main` (not `HEAD`), an interactively chosen React CI build,
and `HEAD` saved as metadata. Firefox source review then could not
reproduce the zip.

Require a clean tree, resolve `HEAD` once, and use that hash for the
source archive, the experimental React download, and the metadata
printed for AMO. Drop the prompt that let those diverge.

Depends on #37305 and #37306.

## How did you test this change?

Build-script only. The release helper now errors on a dirty tree and
threads a single `git rev-parse HEAD` into archive, download, and
metadata.

Co-authored-by: Ruslan Lesiutin <hoxy@meta.com>
The Flight Client copies the debug info of a referenced chunk into the
chunk that references it, so that the receiving chunk records what
blocked it. It copies the entries once per reference, and a referenced
chunk already carries the entries that it received itself. A response
that deduplicates the same object across a chain of rows therefore
multiplies the entries at every step. In development the array
eventually grows past what the engine can allocate for it, and the
client throws `RangeError: Invalid array length`.

The receiving chunk now takes each entry only once. The entries are
copied by reference and never cloned on this path, so a comparison by
identity is exact. The array becomes bounded by the number of distinct
entries in the response rather than by a chosen limit.

The bookkeeping costs one `Set` per chunk that receives debug info, in
development only. The set holds a reference to each entry rather than a
copy, so the entries stay shared and nothing about the debug info is
duplicated. A chunk receives entries only while it is blocked, so the
fix releases the set as soon as the chunk initializes. Debug info still
accumulates transitively, which this change does not alter.

This change also adds the `!reference.isDebug` guards that #37358
proposes for the element props branch and the default branch of
`fulfillReference`. #35795 introduced the rule that a reference resolved
during debug info resolution does not transfer, and it left those two
branches behind. The guards make that rule hold at every branch.
However, those branches reference debug chunks that carry no entries, so
the guards change no observed behaviour, and they do not fix the growth
in #37343, which comes from references in model chunks. #37343 also
reports the call in `getOutlinedModel` as unguarded, which it is not,
because #35795 already skips it there.

The rest of #37358 deduplicates the entries, which is the right
direction, but it scans the receiving array for every candidate, which
is quadratic in the size of the debug info. #37359 caps the array at a
constant instead, which stops the crash but keeps copying the duplicates
and drops debug info once a response passes the cap.

**Alternatives Considered**

- Tracking the referenced chunks rather than the entries would be
cheaper, because it would need one map entry per referenced chunk. It
would not be enough, because a chunk can reach the same entry through
two paths. A chunk can hold a client reference directly and also
reference a chunk that already received the debug info of that client
reference.
- Recording the last receiving chunk on each entry would be exact while
a chunk parses its model, where the transfers into it are consecutive.
It would break once transfers into different chunks interleave, and that
is the path the reported crash takes.
- Turning `_debugInfo` itself into a `Set` is not possible, because the
reconciler, Fizz, the Flight Server and DevTools read it by index and
depend on its order.

Fixes #37343
Closes #37358
Closes #37359

Co-authored-by: sundeep8967 <71071718+sundeep8967@users.noreply.github.com>
…37491)

The warning was previously only enabled for experimental builds
(`react@experimental`). This enables the warning for `react@canary` as
well.

Keep in mind that conditional `use()` is generally supported. This
warning only triggers if the condition is based on `promise.status` (or
`promise.value`). Let `use()` handle that status. React will not suspend
if the `promise.status` is already `'fulfilled'`.

More information can be found in the [`use()` docs under "Don’t skip
calling use based on whether a Promise is already
settled."](https://react.dev/reference/react/use#conditional-use).

We've tested this at Vercel on the latest version of SWR (which
previously had conditional `use()` calls) and found no false-positive
warnings or excessive warnings.
Apply the render's script nonce to import maps emitted through the
`importMap` server rendering option. This keeps configured import maps
compatible with nonce-based Content Security Policies and uses the same
escaped nonce value as other render-managed scripts.
Propagate errors from block lowering instead of continuing with
incomplete HIR.

This prevents the Rust compiler from emitting partial output when it
encounters unsupported syntax, including nested TypeScript `this`
parameters.

This landed earlier as #37232, which exposed an existing FBT diagnostic
ordering issue. Resolve local FBT bindings before checking whether an
`<fbt>` tag comes from a module import, so the compiler reports the
earlier, more useful Todo instead of a later invariant.

Run from the repository root:

- `yarn --cwd compiler workspace babel-plugin-react-compiler-rust test`
- `cargo test --manifest-path compiler/Cargo.toml -p
react_compiler_lowering`
Add myself to maintainers
…nt (#37496)

## Summary
- `react_get_component_by_dom_element` returns the host node for a DOM
element, which never has hooks.
- Stop advertising `hooks?` in the tool description so agents do not
request a field that is never returned.

## Test plan
- [ ] `yarn test --build --project=devtools -r=experimental
DevToolsCdtMcp`
## Summary
- Upgrade the cdt-mcp e2e dependency from chrome-devtools-mcp 1.3.0 to
1.8.0.
- Pass `pageId` to page-scoped CLI tools, which 1.8.0 requires.
- Use a hex `sessionId` (`crypto.randomUUID()`). 1.8.0 rejects ids that
are not `/[a-fA-F0-9-]+/`.

Stacked on #37496

## Test plan
- [ ] `yarn --cwd packages/react-devtools-cdt-mcp test:e2e`
Preserve the RefValue source location when joining mixed ref types so
validation errors point to the original ref access.

Before this change it used to show the incorrect location when using the
rust compiler.

```rust
  2 |   const ref = useRef(null);
  3 |   const x = cond ? ref : ref.current;
> 4 |   return <Foo value={x} />;
    |                      ^ Cannot access ref value during render
  5 | }
  6 |
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

Couldn't load reviewers.

Assignees

Couldn't load assignees.