Skip to content

pulling in latest React - #3

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

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

Conversation

@balazsbajorics

Copy link
Copy Markdown

No description provided.

timneutkens and others added 30 commits February 19, 2026 08:37
…yload (#35776)

## Summary

Follow-up to vercel/next.js#89823 with the
actual changes to React.

Replaces the `JSON.parse` reviver callback in `initializeModelChunk`
with a two-step approach: plain `JSON.parse()` followed by a recursive
`reviveModel()` post-process (same as in Flight Reply Server). This
yields a **~75% speedup** in RSC chunk deserialization.

| Payload | Original (ms) | Walk (ms) | Speedup |
|---------|---------------|-----------|---------|
| Small (2 elements, 142B) | 0.0024 | 0.0007 | **+72%** |
| Medium (~12 elements, 914B) | 0.0116 | 0.0031 | **+73%** |
| Large (~90 elements, 16.7KB) | 0.1836 | 0.0451 | **+75%** |
| XL (~200 elements, 25.7KB) | 0.3742 | 0.0913 | **+76%** |
| Table (1000 rows, 110KB) | 3.0862 | 0.6887 | **+78%** |

## Problem

`createFromJSONCallback` returns a reviver function passed as the second
argument to `JSON.parse()`. This reviver is called for **every key-value
pair** in the parsed JSON. While the logic inside the reviver is
lightweight, the dominant cost is the **C++ → JavaScript boundary
crossing** — V8's `JSON.parse` is implemented in C++, and calling back
into JavaScript for every node incurs significant overhead.

Even a trivial no-op reviver `(k, v) => v` makes `JSON.parse` **~4x
slower** than bare `JSON.parse` without a reviver:

```
108 KB payload:
  Bare JSON.parse:    0.60 ms
  Trivial reviver:    2.95 ms  (+391%)
```

## Change

Replace the reviver with a two-step process:

1. `JSON.parse(resolvedModel)` — parse the entire payload in C++ with no
callbacks
2. `reviveModel` — recursively walk the resulting object in pure
JavaScript to apply RSC transformations

The `reviveModel` function includes additional optimizations over the
original reviver:
- **Short-circuits plain strings**: only calls `parseModelString` when
the string starts with `$`, skipping the vast majority of strings (class
names, text content, etc.)
- **Stays entirely in JavaScript** — no C++ boundary crossings during
the walk

## Results

You can find the related applications in the [Next.js PR
](vercel/next.js#89823 I've been testing this
on Next.js applications.

### Table as Server Component with 1000 items

Before:

```
    "min": 13.782875000000786,
    "max": 22.23400000000038,
    "avg": 17.116868530000083,
    "p50": 17.10766700000022,
    "p75": 18.50787499999933,
    "p95": 20.426249999998618,
    "p99": 21.814125000000786
```

After:

```
    "min": 10.963916999999128,
    "max": 18.096083000000363,
    "avg": 13.543286884999988,
    "p50": 13.58350000000064,
    "p75": 14.871791999999914,
    "p95": 16.08429099999921,
    "p99": 17.591458000000785
```

### Table as Client Component with 1000 items

Before:

```
    "min": 3.888875000000553,
    "max": 9.044959000000745,
    "avg": 4.651271475000067,
    "p50": 4.555749999999534,
    "p75": 4.966624999999112,
    "p95": 5.47754200000054,
    "p99": 6.109499999998661
````

After:

```
    "min": 3.5986250000005384,
    "max": 5.374291000000085,
    "avg": 4.142990245000046,
    "p50": 4.10570799999914,
    "p75": 4.392041999999492,
    "p95": 4.740084000000934,
    "p99": 5.1652500000000146
```

### Nested Suspense

Before:

```
  Requests:  200
  Min:       73ms
  Max:       106ms
  Avg:       78ms
  P50:       77ms
  P75:       80ms
  P95:       85ms
  P99:       94ms
```

After:

```
  Requests:  200
  Min:       56ms
  Max:       67ms
  Avg:       59ms
  P50:       58ms
  P75:       60ms
  P95:       65ms
  P99:       66ms
```

### Even more nested Suspense (double-level Suspense)

Before:

```
  Requests:  200
  Min:       159ms
  Max:       208ms
  Avg:       169ms
  P50:       167ms
  P75:       173ms
  P95:       183ms
  P99:       188ms
```

After:

```
  Requests:  200
  Min:       125ms
  Max:       170ms
  Avg:       134ms
  P50:       132ms
  P75:       138ms
  P95:       148ms
  P99:       160ms
```

## How did you test this change?

Ran it across many Next.js benchmark applications.

The entire Next.js test suite passes with this change.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
…sh (#35824)

When flushing the shell, stylesheets with precedence are emitted in the
`<head>` which blocks paint regardless. Outlining a boundary solely
because it has suspensey CSS provides no benefit during the shell flush
and causes a higher-level fallback to be shown unnecessarily (e.g.
"Middle Fallback" instead of "Inner Fallback").

This change passes a flushingInShell flag to hasSuspenseyContent so the
host config can skip stylesheet-only suspensey content when flushing the
shell. Suspensey images (used for ViewTransition animation reveals)
still trigger outlining during the shell since their motivation is
different.

When flushing streamed completions the behavior is unchanged — suspensey
CSS still causes outlining so the parent content can display sooner
while the stylesheet loads.
Cleans up feature flags that do not have an active experiment and which
we don't currently plan to ship, one commit per flag. Notable removals:
* Automatic (inferred) effect dependencies / Fire: abandoned due to
early feedback. Shipped useEffectEvent which addresses some of the
use-cases.
* Inline JSX transform (experimented, not a consistent win)
* Context selectors (experimented, not a sufficient/consistent win given
the benefit the compiler already provides)
* Instruction Reordering (will try a different approach)

To decide which features to remove, I looked at Meta's internal repos as
well as eslint-pugin-react-hooks to see which flags were never
overridden anywhere. That gave a longer list of flags, from which I then
removed some features that I know are used in OSS.
When a Suspense boundary suspends during initial mount, the primary
children's fibers are discarded because there is no current tree to
preserve them. If the suspended promise never resolves, the only way to
retry is something external like a context change. However, lazy context
propagation could not find the consumer fibers — they no longer exist in
the tree — so the Suspense boundary was never marked for retry and
remained stuck in fallback state indefinitely.

The fix teaches context propagation to conservatively mark suspended
Suspense boundaries for retry when a parent context changes, even when
the consumer fibers can't be found. This matches the existing
conservative approach used for dehydrated (SSR) Suspense boundaries.
Remove dead code left behind after the removal of retryCompileFunction,
enableFire, and inferEffectDependencies:
- Delete ValidateNoUntransformedReferences.ts (always a no-op)
- Remove CompileProgramMetadata type and retryErrors from ProgramContext
- Remove 'client-no-memo' output mode
- Change compileProgram return type from CompileProgramMetadata | null
to void
Add detailed plan for making the React Compiler fault-tolerant by
accumulating errors across all passes instead of stopping at the first
error. This enables reporting multiple compilation errors at once.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35872).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* #35877
* #35876
* #35875
* #35874
* #35873
* __->__ #35872
…ent (#35873)

Add error accumulation methods to the Environment class:
- #errors field to accumulate CompilerErrors across passes
- recordError() to record a single diagnostic (throws if Invariant)
- recordErrors() to record all diagnostics from a CompilerError
- hasErrors() to check if any errors have been recorded
- aggregateErrors() to retrieve the accumulated CompilerError
- tryRecord() to wrap callbacks and catch CompilerErrors

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35873).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* #35877
* #35876
* #35875
* #35874
* __->__ #35873
…erance (#35874)

- Change runWithEnvironment/run/compileFn to return
Result<CodegenFunction, CompilerError>
- Wrap all pipeline passes in env.tryRecord() to catch and record
CompilerErrors
- Record inference pass errors via env.recordErrors() instead of
throwing
- Handle codegen Result explicitly, returning Err on failure
- Add final error check: return Err(env.aggregateErrors()) if any errors
accumulated
- Update tryCompileFunction and retryCompileFunction in Program.ts to
handle Result
- Keep lint-only passes using env.logErrors() (non-blocking)
- Update 52 test fixture expectations that now report additional errors

This is the core integration that enables fault tolerance: errors are
caught,
recorded, and the pipeline continues to discover more errors.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35874).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* #35877
* #35876
* #35875
* __->__ #35874
…rs on env (#35875)

Update 9 validation passes to record errors directly on fn.env instead
of
returning Result<void, CompilerError>:
- validateHooksUsage
- validateNoCapitalizedCalls (also changed throwInvalidReact to
recordError)
- validateUseMemo
- dropManualMemoization
- validateNoRefAccessInRender
- validateNoSetStateInRender
- validateNoImpureFunctionsInRender
- validateNoFreezingKnownMutableFunctions
- validateExhaustiveDependencies

Each pass now calls fn.env.recordErrors() instead of returning
errors.asResult().
Pipeline.ts call sites updated to remove tryRecord() wrappers and
.unwrap().

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35875).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* #35877
* #35876
* __->__ #35875
… tolerance (#35876)

Update remaining validation passes to record errors on env:
- validateMemoizedEffectDependencies
- validatePreservedManualMemoization
- validateSourceLocations (added env parameter)
- validateContextVariableLValues (changed throwTodo to recordError)
- validateLocalsNotReassignedAfterRender (changed throw to recordError)
- validateNoDerivedComputationsInEffects (changed throw to recordError)

Update inference passes:
- inferMutationAliasingEffects: return void, errors on env
- inferMutationAliasingRanges: return Array<AliasingEffect> directly,
errors on env

Update codegen:
- codegenFunction: return CodegenFunction directly, errors on env
- codegenReactiveFunction: same pattern

Update Pipeline.ts to call all passes directly without tryRecord/unwrap.
Also update AnalyseFunctions.ts which called
inferMutationAliasingRanges.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35876).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* #35877
* __->__ #35876
)

Add test fixture demonstrating fault tolerance: the compiler now reports
both a mutation error and a ref access error in the same function, where
previously only one would be reported before bailing out.

Update plan doc to mark all phases as complete.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35877).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* __->__ #35877
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35878).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* __->__ #35878
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35879).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* __->__ #35879
…ng (#35880)

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35880).
* #35888
* #35884
* #35883
* #35882
* #35881
* __->__ #35880
…ing throws (#35881)

Remove `tryRecord()` from the compilation pipeline now that all passes
record
errors directly via `env.recordError()` / `env.recordErrors()`. A single
catch-all try/catch in Program.ts provides the safety net for any pass
that
incorrectly throws instead of recording.

Key changes:
- Remove all ~64 `env.tryRecord()` wrappers in Pipeline.ts
- Delete `tryRecord()` method from Environment.ts
- Add `CompileUnexpectedThrow` logger event so thrown errors are
detectable
- Log `CompileUnexpectedThrow` in Program.ts catch-all for non-invariant
throws
- Fail snap tests on `CompileUnexpectedThrow` to surface pass bugs in
dev
- Convert throwTodo/throwDiagnostic calls in HIRBuilder (fbt, this),
  CodegenReactiveFunction (for-in/for-of), and BuildReactiveFunction to
  record errors or use invariants as appropriate
- Remove try/catch from BuildHIR's lower() since inner throws are now
recorded
- CollectOptionalChainDependencies: return null instead of throwing on
  unsupported optional chain patterns (graceful optimization skip)

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35881).
* #35888
* #35884
* #35883
* #35882
* __->__ #35881
…env.recordError() (#35882)

Removes unnecessary indirection in 17 compiler passes that previously
accumulated errors in a local `CompilerError` instance before flushing
them to `env.recordErrors()` at the end of each pass. Errors are now
emitted directly via `env.recordError()` as they're discovered.

For passes with recursive error-detection patterns
(ValidateNoRefAccessInRender,
ValidateNoSetStateInRender), the internal accumulator is kept but
flushed
via individual `recordError()` calls. For InferMutationAliasingRanges,
a `shouldRecordErrors` flag preserves the conditional suppression logic.
For TransformFire, the throw-based error propagation is replaced with
direct recording plus an early-exit check in Pipeline.ts.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35882).
* #35888
* #35884
* #35883
* __->__ #35882
Rename `state: Environment` to `env: Environment` in
ValidateMemoizedEffectDependencies visitor methods, and
`errorState: Environment` to `env: Environment` in
ValidatePreservedManualMemoization's validateInferredDep.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35883).
* #35888
* #35884
* __->__ #35883
…35884)

Fix the transformFire early-exit in Pipeline.ts to only trigger on new
errors from transformFire itself, not pre-existing errors from earlier
passes. The previous `env.hasErrors()` check was too broad — it would
early-exit on validation errors that existed before transformFire ran.

Also add missing blank line in CodegenReactiveFunction.ts Context class,
and fix formatting in ValidateMemoizedEffectDependencies.ts.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35884).
* #35888
* __->__ #35884
…doc (#35888)

Add concise fault tolerance documentation to CLAUDE.md and the passes
README covering error accumulation, tryRecord wrapping, and the
distinction between validation vs infrastructure passes. Remove the
detailed planning document now that the work is complete.
## Summary

For apps that use AMD, we need to actually `require()` the
ReactDevToolsBackend and load it from the AMD module cache. This adds a
check for the case where the `ReactDevToolsBackend` isn't defined
globally, and so we load it with `require()`.


## How did you test this change?

Tested through #35886
… different host/port/path (#35886)

## Summary

This enables routing the React Dev Tools through a remote server by
being able to specify host, port, and path for the client to connect to.
Basically allowing the React Dev Tools server to have the client connect
elsewhere.

This setups a `clientOptions` which can be set up through environment
variables when starting the React Dev Tools server.

This change shouldn't affect the traditional usage for React Dev Tools.

EDIT: the additional change was moved to another PR 

## How did you test this change?

Run React DevTools with 
```
$ REACT_DEVTOOLS_CLIENT_HOST=<MY_HOST> REACT_DEVTOOLS_CLIENT_PORT=443 REACT_DEVTOOLS_CLIENT_USE_HTTPS=true REACT_DEVTOOLS_PATH=/__react_devtools__/ yarn start

```

Confirm that my application connects to the local React Dev Tools
server/instance/electron app through my remote server.
If a function is known to freeze its inputs, and captures refs, then we
can safely assume those refs are not mutated during render.

An example is React Native's PanResponder, which is designed for use in
interaction handling. Calling `PanResponder.create()` creates an object
that shouldn't be interacted with at render time, so we can treat it as
freezing its arguments, returning a frozen value, and not accessing any
refs in the callbacks passed to it. ValidateNoRefAccessInRender is
updated accordingly - if we see a Freeze <place> and ImmutableCapture
<place> for the same place in the same instruction, we know that it's
not being mutated.

Note that this is a pretty targeted fix. One weakness is that we may not
always emit a Freeze effect if a value is already frozen, which could
cause this optimization not to kick in. The worst case there is that
you'd just get a ref access in render error though, not miscompilation.
And we could always choose to always emit Freeze effects, even for
frozen values, just to retain the information for validations like this.
## Summary

This flag enables React's integration with the browser [Trusted Types
API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API).

The Trusted Types API is a browser security feature that helps prevent
DOM-based XSS attacks. When a site enables Trusted Types enforcement via
`Content-Security-Policy: require-trusted-types-for 'script'`, the
browser requires that values passed to DOM injection sinks (like
`innerHTML`) are typed objects (`TrustedHTML`, `TrustedScript`,
`TrustedScriptURL`) created through developer-defined sanitization
policies, rather than raw strings.

 ### What changed

Previously, React always coerced values to strings (via `'' + value`)
before passing them to DOM APIs like `setAttribute` and `innerHTML`.
This broke Trusted Types because it converted typed objects into plain
strings, which the browser would then reject under Trusted Types
enforcement.

React now passes values directly to DOM APIs without string coercion,
preserving Trusted Types objects so the browser can validate them. This
applies to `dangerouslySetInnerHTML`, all HTML and SVG attributes, and
URL attributes (`href`, `action`, etc).

 ### Before (broken)

Using Trusted Types with something like`dangerouslySetInnerHTML` would
throw:

 ```js
 const sanitizer = trustedTypes.createPolicy('sanitizer', {
   createHTML: (input) => DOMPurify.sanitize(input),
 });

 function Comment({text}) {
   const clean = sanitizer.createHTML(text);
   // clean is a TrustedHTML object, but React would call '' + clean,
   // converting it back to a plain string before setting innerHTML.
   // Under Trusted Types enforcement, the browser rejects the string:
   //
   //   TypeError: Failed to set 'innerHTML' on 'Element':
   //   This document requires 'TrustedHTML' assignment.
   return <div dangerouslySetInnerHTML={{__html: clean}} />;
 }
 ```

### After (works)

React now passes the TrustedHTML object directly to the DOM without
stringifying it:

```js
 const policy = trustedTypes.createPolicy('sanitizer', {
   createHTML: (input) => DOMPurify.sanitize(input),
 });

 function Comment({text}) {
   // TrustedHTML objects are passed directly to innerHTML
   return <div dangerouslySetInnerHTML={{__html: policy.createHTML(text)}} />;
 }

 function UserProfile({bio}) {
   // String attribute values also preserve Trusted Types objects
   return <div data-bio={policy.createHTML(bio)} />;
 }
 ```

 ## Non-breaking change

 - Sites using Trusted Types: React no longer breaks Trusted Types enforcement. TrustedHTML and TrustedScriptURL objects passed through React props are forwarded to the DOM without being stringified.
 - Sites not using Trusted Types: No behavior change. DOM APIs accept both strings and Trusted Types objects, so removing the explicit string coercion is functionally identical.
## Summary

This fixes the semantics of the `timeStamp` property of events in React
Native.

Currently, most events just assign `Date.now()` (at the time of creating
the event object in JavaScript) as the `timeStamp` property. This is a
divergence with Web and most native platforms, that use a monotonic
timestamp for the value (on Web, the same timestamp provided by
`performance.now()`).

Additionally, many native events specify a timestamp in the event data
object as `timestamp` and gets ignored by the logic in JS as it only
looks at properties named `timeStamp` specifically (camel case).

This PR fixes both issues by:
1. Using `performance.now()` instead of `Date.now()` by default (if
available).
2. Checking for a `timestamp` property before falling back to the
default (apart from `timeStamp`).

## How did you test this change?

Added unit tests for verify the new behavior.
## Summary

This PR updates the event dispatching logic in React Native to expose
the dispatched event in the global scope as done on Web
(https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke) and
in the new implementation of `EventTarget` in React Native
(https://github.com/facebook/react-native/blob/d1b2ddc9cb4f7b4cb795fed197347173ed5c4bfb/packages/react-native/src/private/webapis/dom/events/EventTarget.js#L372).

## How did you test this change?

Added unit tests
…and ValidatePreservedManualMemoization (#35917)

With the recent changes to make the compiler fault tolerant and always
continue through all passes, we can now sometimes report duplicative
errors. Specifically, when `ValidateExhaustiveDependencies` finds
incorrect deps for a useMemo/useCallback call,
`ValidatePreservedManualMemoization` will generally also error for the
same block, producing duplicate errors. The exhaustive deps error is
strictly more informative, so if we've already reported the earlier
error we don't need the later one.

This adds a `hasInvalidDeps` flag to StartMemoize that is set when
ValidateExhaustiveDependencies produces a diagnostic.
ValidatePreservedManualMemoization then skips validation for memo blocks
with this flag set.
Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Been enabled in stable for quite a while, also rolled out at Meta.
gnoff and others added 30 commits July 21, 2026 09:26
Security Patches included in 19.2.8

Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
…unks (#37095)

Turbopack has introduced a new format for chunks (experimental):

```typescript
type ChunkUrlOrMerged = ChunkUrl | [ChunkUrl, ChunkPath[], number[]]
```

This will allow us to dynamically choose to merge / unmerge chunks based
on already loaded chunks. See
vercel/next.js#95261 for more context behind
these feature.

However, we noticed that this breaks `prepareDestinationWithChunks()` on
server-side renders. The prepared chunk script tags were a stringified
version of this array:

<img width="1758" height="474" alt="Screenshot 2026-07-22 at 9 37 06 am"
src="https://github.com/user-attachments/assets/fa273b44-5942-4037-b955-4338472242fe"
/>

Instead, we should prepare the merged chunk as these SSR-d pages would
not have any loaded chunks tracked in memory (that happens on the
client).

This PR brings back this optimisation and also would remove the
console-logged errors associated with them.


---------

Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
Tightens `EventEmitter` listener types and fixes error handling so the
first thrown value is preserved while subsequent errors are reported
instead of swallowed. Adds regression coverage for listener failures.
Ensures the standalone DevTools Bridge fully shuts down when its
WebSocket closes, with re-entrancy protection. Adds tests confirming
event-only shutdown leaves the Bridge active while socket closure shuts
it down.
Builds on #37048 by replacing `any`-based Bridge and Wall boundaries
with typed `mixed` values and explicit runtime validation. Invalid
messages and post-shutdown operations now throw, while shutdown reliably
flushes queued messages even if cleanup fails.

Strengthens the DevTools Bridge and Wall contracts:
- Models event dictionaries as event-to-payload maps, using `void` for
events without payloads.
- Types `send(event, payload?)` directly, eliminating runtime
payload-arity handling.
- Replaces broad `any` transport types with `mixed` and boundary
validation.
- Throws on invalid lifecycle usage instead of warning or silently
returning.
- Ensures shutdown flushes queued messages even when Wall cleanup fails.
  - Updates Wall implementations and adds Bridge lifecycle coverage.
Builds on #37049 by validating Store operation invariants before
mutation. Missing nodes, invalid element types, inconsistent
parent-child relationships, and invalid reorder operations now emit and
throw explicit errors instead of silently continuing with corrupted
state.

Adds a canonical-render regression test for invalid child removal.
Buffers Bridge messages during extension port reconnects and adds a
readiness handshake for ordered queue flushing. Includes regression
coverage for reconnect delivery and listener cleanup.

Potential scenario could be a long user session, where Chrome kills one
of the extension ports to save resources and then user re-connects by
navigating back to the DevTools UI.
Removing attributes does not actually reset all property state on the
singleton instance. It also has the side effect of wiping any 3rd party
set attributes and properties that 3rd party scripts and extensions.

Reimplements the singleton release to clear properties on the instance
based on the last committed props for the singleton fiber.

Notably there is an unfixed path with preamble contribution markers that
still just clears based on attributes which covers SSR'd attributes that
need to be wiped before client recovery on hydration can continue. There
are gated failing tests for this as a TODO.
Things I found while integrating this bench into
vercel/next.js#96234.

See code.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…37113)

Stacked on #37112

In dev effects are validated using a double-invoke technique. To do this
the effects are destroyed and recreated during a validation traversal.
However this same disappear and reappear path is used when Offscreen
Fibers go hidden and HostSingletons have unique behavior when going
hidden. So now that the double invoke effects process happens during
hydration it is more common to have your Singletons be released and
acquired during this validation phase which is observable most notably
by having extra attributes removed from them. The prior commits in this
stack deal with preserving non-react owned attributes on release however
it is semantically incorrect to release and acquire the singleton during
this validation because it isn't really an effect, it simply lives in
this traversal to avoid having to do another traversal during the
commit.

This change adds a bit of info to the release and acquire path to only
conditionally perform the necessary reacquire flow if we are not in the
validation phase.
…7109)

[Secure Ecmascript](https://github.com/tc39/proposal-ses) would freeze
the prototype of intrinsics. Since `ReactPromise` inherits the prototype
from `Promise`, it also copies over the writable definition.

Using `defineProperty` on an inherited property is compatible with SES
though. That's also closer to how classes are specced in JS.


---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…#36986)

## Summary

Migrate internal `Libraries/` import subpaths from `react-native` to the
incoming dedicated entry points in React Native 0.87.

**Motivation**

See [**RFC0894: Removing deep imports from
react-native**](react-native-community/discussions-and-proposals#894)

**Changes**

The `react-private-interface` entry point has been created explicitly
for the private contract between React ↔ React Native, and is 1:1 with
the previous `ReactNativePrivateInterface` module.

Both of the below `Libraries/` paths still exist, but are deprecated —
this is a lagging migration that will enable cleanup in a future RN
version.

- `react-native/Libraries/Core/InitializeCore` →
`react-native/setup-env`
[react/react-native#57475](react/react-native#57475)
- `react-native/Libraries/ReactPrivate/ReactNativePrivateInterface` →
`react-native/react-private-interface`
[react/react-native#57495](react/react-native#57495)

## Test Plan

Flow
…tree (#37135)

Closes vercel/next.js#95848.

Fixes a hang: if an update changes what's inside a server-rendered
Suspense or Activity boundary before that boundary has hydrated, and the
affected content is hidden, React stops committing. The new content
renders once its data arrives, but the render is discarded every time,
nothing is scheduled, and nothing ever pings — the update never lands
and the app appears frozen.

"Hidden" means either of two things, and there's a test for each:

- the update itself hides a dehydrated `<Activity>` (while mounting new
sibling content that suspends), or
- the dehydrated boundary is inside the primary tree of a parent
boundary that just suspended and is showing its fallback.

This is how we found it in practice.

With vercel/next.js#95682, pressing Back before
hydration finishes made the router replay the missed navigation from its
first effect. This worked fine outside of Cache Components, but in Cache
Components mode (which turns on Activity), the old page's Activity
(still dehydrated) gets hidden, the new page's content suspends inside
the layout's Suspense boundary, and after the data arrives the page
stays blank forever.

As a result, vercel/next.js#95682 got reverted.
If we fix this, we can unrevert it.

## Why it happens

When an update changes a dehydrated boundary, we schedule a render at a
higher priority to hydrate it before the update applies. If we already
tried that, we give up and client render, but mark the render as
suspended so it doesn't commit while the hydration attempt might still
finish first.

Both steps assume the attempt can actually run. Inside a hidden tree it
can't, because updates in hidden trees are deferred until the tree is
revealed. The scheduled attempt never runs but still consumes the retry
lane, which sends every later render into the give-up path — and the
give-up path keeps discarding finished renders, waiting for a hydration
attempt that isn't in flight. Once the last piece of data resolves
there's nothing left to ping us awake. The root ends up with
`pendingLanes === suspendedLanes`, `pingedLanes` empty, and no callback
scheduled.

The update doesn't need to be sync or discrete: a plain setState from an
effect is enough. Wrapping the same update in `startTransition` avoids
it, which is probably why this went unnoticed.

## The fix

If the boundary is inside a hidden tree (`isCurrentTreeHidden()`), skip
the hydration attempt and client render right away. There's nothing
visible to protect: replacing hidden server HTML doesn't show, and the
replacement children render when the tree is revealed.

One behavior note: this discards the hidden server HTML instead of
preserving it for later hydration on reveal, same as the existing
give-up path. Keeping it dehydrated and hydrating at reveal would be a
nicer follow-up, but needs commit-phase support that doesn't exist
today.

## How did you test this change?

The first commit adds failing tests for both boundary types; the fix
makes them pass. `startTransition` variants of the same scenarios are
included as passing controls. Ran the Activity, partial/selective
hydration, Fizz, Suspense, and Offscreen suites in both release
channels.
## Summary

`FragmentInstance.blur()` only matched the active element against the
first level of host children. If the focused element was nested inside
one of those children, `focus()` could reach it but `blur()` would leave
it focused.

This treats an active element contained by a Fragment host child as part
of the Fragment and blurs the active element itself. It also adds
regression coverage for a nested input.

Fixes #37124.

## How did you test this change?

- `yarn test ReactDOMFragmentRefs-test --runInBand` (65 tests passed)
- `yarn test --prod ReactDOMFragmentRefs-test --runInBand` (65 tests
passed)
- `yarn prettier`
- `yarn linc`
- `yarn flow dom-node`
Additional defense-in-depth in case consumers pass untrusted input into
Flight Client.

Flight Client generally assumes trusted input.

We'll reserve these kind of fixes for Flight Client in case the
untrusted input leads to catastrophic vulnerabilities e.g. prototype
pollutions that can be used for remote code executions.
## Summary

Adds a new API to `react-dom` called `browser()`.

`browser()` returns a "usable" that will error during SSR and resolve
during rendering in the browser. The purpose is to allow you to express
the idea that a component should suspend on the server but not in the
browser. The method is not available inside a `react-server`
environment. This is a client only feature.

This is a `react-dom` API because the concept of browser doesn't apply
generally to React itself.

This codifies a pattern that is common in some apps where you error
during SSR to prevent rendering some component on the server and you end
up suppressing the error that is reported in the client to avoid this
appearing like a problem rather than intended behavior. Unfortunately
this is not an option for many because hacking around to prevent errors
from being logged is not practical for many

By making this a React API we enabled this common pattern in any React
using library or application

```tsx
import {use, Suspense} from 'react';
import {browser} from 'react-dom';

function BrowserOnly() {
  use(browser());
  return <ClientContent />;
}

function App() {
  return (
    <Suspense fallback={<Fallback />}>
      <BrowserOnly />
    </Suspense>
  );
}
```

It is an error to `use(browser())` outside of a Suspense boundary
because you cannot recover from the root. this restriction may be lifted
in the future but is part of the current limitations of the API

## Implementation

Deferring rendering to a downstream system is modeled in React already
as recoverable errors. The idea is that in some environments you might
not want to report something directly as an error because a later
environment has an opportunity to recover from it without alerting the
user to the mishap. This concept also shows up in RSC with halted
references. They can "recover" in a later render by eventually resolving
to some value.

To model the idea of "render in the browser" we are really just modeling
an intentional recoverable error. However since you don't want to treat
this kind of error as exceptional we intentionally suppress logging.
Additionally since aborting a server render is semantically equivalent
to "erroring" in every unfinished task we also support aborting with a
`browser()` so you can describe ending a stream with intentional holes
that won't be logged as errors in the browser when hydrating.

One interesting thing we do with this particular API is it returns an
object that is isomorphic and it's the `use` or `abort` function that
handles differing behaviors. This means you can create these objects in
module scope and use them even in complex scenarios like server
rendering inside the browser while React is rendering.

This implementation is flagged so we can disable the feature quickly if
we decide to not ship this in a stable. It is going into React
unprefixed for now because the semantics are clear and the utility is
widely known.

## Alternatives

We considered `useBrowser()` or a similar hook however this means you
must call it unconditionally. There are use cases where props might
influence whether you want to allow something to render during SSR or
not. for instance you might have a data fetching library that accepts
initial data on the server but if it doesn't receive initial data it
falls back to browser only rendering.

Another consideration is a throwing function like just calling
`browser()` would throw if called during an SSR render. The main reason
we do not think this is a good idea is because you can then call this
arbitrarily deep and the throw can be caught and might be suppressed
accidentally. By making it a usable it can only be done in hooks or
hook-like contexts.
I've been triaging some lifecycle races during sessions with BFCache
involved and noticed a few type errors in real world scenario. These
objects were not properly typed, fixing in this PR.
The main reason why I am doing this is because there is no API in the
browser to "unmount" the created panel.

If you have React DevTools installed, we should always create a panel,
but the contents of the panel should be dynamically populated based on
the target. If it is not a React app, we will continue showing the stub
message.

Previously, we wouldn't mount a panel at all, and historically we've
received a few reports of this as a bug.
I have noticed inconsistency errors being thrown during browser
navigations that involve entries from BFCache. The main argument on why
this could be affecting React DevTools backend lifecycle is the fact
that Chrome kills the port manually, while freezing and preserving the
JavaScript heap -
https://developer.chrome.com/blog/bfcache-extension-messaging-changes.

Basically, we could end up in a permutation, where port is dead, but
Backend / Agent are alive. Such setup is not expected by React DevTools.

On `main`:


https://github.com/user-attachments/assets/9ca10286-b545-4384-bd6b-33d9a4ddde3d


With these changes:


https://github.com/user-attachments/assets/c4639c4a-6385-4248-a5c4-a39895d7fff6


I couldn't come up with a good test for this yet, but I will try to add
something. I am not convinced yet that emulating `pagehide` / `pageshow`
would be sufficient to reproduce browser environment during BFCache
entries.
This is a cherry-pick of #34030, with
a feature flag gating and a test coverage.

The flag is disabled by default and dynamic for FB builds to understand
first how noisy this warning can be.

---

See #34030 for more context on the
change.
Added behind a new experimental flag, `enableFlightWeakThenables`.

Adds a new thenable status to the Flight protocol: `'pending_weak'`.
Unlike a regular pending thenable, a weak thenable does not block the
stream from closing. If it settles while the stream is still open, its
value is emitted like a normal pending thenable. Otherwise its reference
is left unfulfilled and on the client it stays forever pending, without
erroring, even when the connection closes. It's up to the client to
handle the unresolved promise in an appropriate way.

The motivating use case is being able to encode metadata about a Flight
stream into the response itself. For example, a framework might want to
track whether a page varies by search params. It could represent this in
the response as a `Promise<boolean>` that resolves to `true` as soon as
the component being rendered in the stream accesses search params. If
the thenable never resolves by the time the stream closes, then the
client knows that no search params were ever accessed.

In the future we could add a higher-level API for encoding this kind of
information. For now, we intentionally start with the low-level
primitive so frameworks can experiment in userspace without adding
significantly to React's surface area.

Internally, Flight already uses its own private thenable statuses, like
`'resolved_model'`, and the protocol is designed to treat any status
besides `'fulfilled'` and `'rejected'` as equivalent to `'pending'`, so
`'pending_weak'` slots into the existing machinery. On the wire, a weak
reference is encoded as `$w<id>`, next to `$@<id>` for regular promises,
so the client knows its row may intentionally never arrive. On the
client, a weak reference behaves like any other pending promise until
the response closes; then, instead of erroring, it is left forever
pending.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Previously, every instance of ErrorBoundary, which wraps every custom
panel in extension, was subscribing to errors from the Store. This would
report the same error for every mounted panel.

ErrorBoundary now only intercepts render-time errors, and Store errors
are captured and reported in an external subscription at the place where
Store is created.
…acks (#37186)

Cleaning up the Timeline profiler in the next commit on top of this one.

If user is debugging React 19.2+, we will show a suggestion to record a
trace on Performance panel. Otherwise, we will suggest to upgrade to
React 19.2 to unlock Performance tracks.

<img width="751" height="832" alt="Screenshot 2026-08-03 at 14 57 43"
src="https://github.com/user-attachments/assets/153e712b-8f7c-4ec5-87f8-b01cf1180aae"
/>
## Summary

With the tab gone, everything that fed it is unreachable. This deletes
`packages/react-devtools-timeline` (74 files) and the backend that
produced its data, `backend/profilingHooks.js`, along with
`SidebarEventInfo`, the two timeline test suites, the `timelineData`
snapshot serializer, and the scheduling-profiler fixture.

It also unwires the plumbing that only existed to carry timeline data:
`recordTimeline` across the reload-and-profile path (hook →
sessionStorage → agent → renderer), `timelineData` on
`ProfilingDataBackend` and the profile export, the `supportsTimeline`
Store config, the `rootSupportsTimelineProfiling` capability, the
`DevToolsProfilingHooks` type and the `ReactRenderer` members DevTools
used to inject it, the 40 `--color-timeline-*` theme variables in both
themes plus the orphaned `--color-scroll-caret`, and `hook.js`'s
internal-module-range tracking with its `react-devtools-facade` stubs.

`yarn.lock` is regenerated: 52 distinct package-versions and 68
requirement specs drop out, with no additions and no version changes to
anything that remains.

## Deliberate non-changes

- **`PROFILER_EXPORT_VERSION` stays at 5.**
`prepareProfilingDataFrontendFromExport` compares versions with `!==`,
so a bump would reject every profile anyone has already saved.
`timelineData` was an optional key, so dropping it is invisible in both
directions.
- **Profiling flag bit `0b010` is retired, not reused**, and the
constant is replaced by a comment saying so. Shipped backends keep
setting it, so renumbering `PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT`
into that slot would make a new frontend misread older backends as
tracks-capable.
- **The `displayName` properties on DevTools' cache thenables are
kept.** They look timeline-only, but `ReactFiberThenable` reads
`thenable.displayName` to name I/O in async debug info, which feeds the
Performance tracks. Only their stale comments are corrected.
- **`react-reconciler`, `shared/ReactFeatureFlags.js` and
`scripts/rollup` are untouched**; `enableSchedulingProfiler` is still
live for www and native-fb.

## Follow-ups (not in this stack)

Three stale comments still name the removed package:
`scripts/rollup/wrappers.js:532` and `ReactFiberLane.js:38,125`. Left
alone to keep this stack purely DevTools-side.

## Test plan

`yarn linc`, `yarn flow dom-node`, and the DevTools suite all pass on
this commit in isolation (40/40 suites, 582 tests).
## Summary

Added the search by component name functionality as requested for
#32995 (comment)


Adds a component search to the Profiler's commit view, so you can find a
specific component within the currently selected commit (Flamegraph &
Ranked charts). Previously the only search lived in the Components panel
and covered the live tree, not profiling data.

Behavior is inspired from Chrome DevTools' in-page find:

- Cmd/Ctrl+F opens a collapsible search box floating over the chart (no
always-on input).
- Shows an N | M match count; ↑/↓ buttons and Enter / Shift+Enter step
through matches (with wraparound).
- Each match is selected via the existing selectFiber, so it highlights,
zooms, updates the sidebar, syncs to the Components tab, and scrolls
into view.
- Esc or ✕ closes it.
- Search is scoped to the selected commit only — never the whole trace.
Switching commits re-scopes the count.

## How did you test this change?



https://github.com/user-attachments/assets/ab2396e1-f329-4213-b053-9b3d08988c6b
…el (#37203)

Enables the flag for OSS builds in experimental to allow
experimentation. We're interested in trying this out on Vercel since
we're a heavy SWR user.
## Summary

There was a bug in the helper that unwraps component names like
Forget(Memo(Button)) into a base component name plus its HOC wrappers.
The regex was using the g flag, which means exec() remembers its
position via lastIndex. Since each iteration replaces the current string
with the shorter unwrapped inner string, lastIndex ends up pointing past
the end of the new string. The next exec() returns null, so the loop
stops after unwrapping only the outermost HOC.

component named Forget(Memo(ForgetMemoCounter))
  before fixes   ✨Memo(ForgetMemoCounter)
  after  fixes   ✨🧠ForgetMemoCounter

component named Forget(ForwardRef(ForgetForwardRefCounter))
  before  fixes ✨ForwardRef(ForgetForwardRefCounter)
  after  fixes  ✨ForgetForwardRefCounter


## How did you test this change?

Tested the change locally in `devtool` and added tests for the same

**Before**
<img width="1920" height="690" alt="devtools-hoc-BEFORE-buggy"
src="https://github.com/user-attachments/assets/14e7e632-da97-43fb-867b-9da3b9d7cb22"
/>

**After**
<img width="1920" height="690" alt="devtools-hoc-AFTER-fixed"
src="https://github.com/user-attachments/assets/4fe91b78-af11-4584-986b-b7aa9a03d0b6"
/>


Not sure if we need a new fixture can add one if required
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.