Commit Graph
1254 Commits
Author SHA1 Message Date
josephsavona 80e4d87f06 [compiler] Improve IIFE inlining (#33726)
We currently inline IIFEs by creating a temporary and a labeled block w
the original code. The original return statements turn into an
assignment to the temporary and break out of the label. However, many
cases of IIFEs are due to inlining of manual `useMemo()`, and these
cases often have only a single return statement. Here, the output is
cleaner if we avoid the temporary and label - so that's what we do in
this PR.

Note that the most complex part of the change is actually around
ValidatePreserveExistingMemo - we have some logic to track the IIFE
temporary reassignmetns which needs to be updated to handle the simpler
version of inlining.

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

DiffTrain build for [956d770adf](https://github.com/facebook/react/commit/956d770adf59e1f8a00a7b7c52b5727ef9e353e7)
2025-07-08 16:43:32 -07:00
gnoff c2297075f5 [Fizz] Render preamble eagerly (#33730)
We unnecessarily render the preamble in a task. This updates the
implementation to perform this render inline.

Testing this is tricky because one of the only ways you could assert
this was even happening is based on how things error if you abort while
rendering the root.

While adding a test for this I discovered that not all abortable tasks
report errors when aborted during a normal render. I've asserted the
current behavior and will address the other issue at another time and
updated the assertion later as necessary

DiffTrain build for [befc1246b0](https://github.com/facebook/react/commit/befc1246b07a04b401bc6e914b7f336a442dca1a)
2025-07-08 08:30:51 -07:00
sebmarkbage 0efbfeb94e [Fiber] Name content inside "Suspense fallback" (#33724)
Same as #33723 but for Fiber.

DiffTrain build for [b44a99bf58](https://github.com/facebook/react/commit/b44a99bf58d69d52b5288d9eadcc6d226d705e11)
2025-07-07 21:07:54 -07:00
rickhanlonii a357246f8c Finally remove favorSafetyOverHydrationPerf (#33619)
This is rolled out to 100%.

Let me merge it though.

DiffTrain build for [e43986f1f3](https://github.com/facebook/react/commit/e43986f1f3e6e94f7eef86fb21c830959e0ade6c)
2025-07-07 11:04:35 -07:00
rickhanlonii 0aa0c27c92 [tests] Assert on component stack for Maximum Update error (#33686)
Good to assert these include the component stack

DiffTrain build for [e4314a0a0f](https://github.com/facebook/react/commit/e4314a0a0f6ea1ab8f739c9119583d148c1be657)
2025-07-07 11:03:50 -07:00
sebmarkbage 56b1e5f29b [Fizz] Name content inside "Suspense fallback" (#33723)
Content in Suspense fallbacks are really not considered part of the
Suspense but since it does have some behavior it should be marked
somehow separately from the Suspense content.

A follow up would be to do the same in Fiber.

DiffTrain build for [c932e45780](https://github.com/facebook/react/commit/c932e457800f077352b1b322f14a796e589299fb)
2025-07-07 10:57:47 -07:00
sebmarkbage 216c636262 [Fiber] Highlight a Component with Deeply Equal Props in the Performance Track (#33660)
Stacked on #33658 and #33659.

If we detect that a component is receiving only deeply equal objects,
then we highlight it as potentially problematic and worth looking into.

<img width="1055" alt="Screenshot 2025-06-27 at 4 15 28 PM"
src="https://github.com/user-attachments/assets/e96c6a05-7fff-4fd7-b59a-36ed79f8e609"
/>

It's fairly conservative and can bail out for a number of reasons:

- We only log it on the first parent that triggered this case since
other children could be indirect causes.
- If children has changed then we bail out since this component will
rerender anyway. This means that it won't warn for a lot of cases that
receive plain DOM children since the DOM children won't themselves get
logged.
- If the component's total render time including children is 100ms or
less then we skip warning because rerendering might not be a big deal.
- We don't warn if you have shallow equality but could memoize the JSX
element itself since we don't typically recommend that and React
Compiler doesn't do that. It only warns if you have nested objects too.
- If the depth of the objects is deeper than like the 3 levels that we
print diffs for then we wouldn't warn since we don't know if they were
equal (although we might still warn on a child).
- If the component had any updates scheduled on itself (e.g. setState)
then we don't warn since it would rerender anyway. This should really
consider Context updates too but we don't do that atm. Technically you
should still memoize the incoming props even if you also had unrelated
updates since it could apply to deeper bailouts.

DiffTrain build for [0b78161d7d](https://github.com/facebook/react/commit/0b78161d7d76b7fb9786f25dd222010b9e417191)
2025-07-02 14:39:23 -07:00
sebmarkbage e05a926ec6 Clear width/height from Keyframes to Optimize View Transitions (#33576)
View Transitions has this annoying quirk where it adds `width` and
`height` to keyframes automatically when generating keyframes even when
it's not needed. This causes them to deopt from running on the
compositor thread in both Chrome and Safari. @bramus has a [good article
on
it](https://www.bram.us/2025/02/07/view-transitions-applied-more-performant-view-transition-group-animations/).

In React we can automatically rewrite the keyframes when we're starting
a View Transition to drop the `width` and `height` from the keyframes
when they have the same value and the same value as the pseudo element.

To compare it against the pseudo element we first apply the new
keyframes without the width/height and then read it back to see if it
has changed. For gestures, we have already cancelled the previous
animation so we can just read out from that.

DiffTrain build for [c0d151ce7e](https://github.com/facebook/react/commit/c0d151ce7ed9ebdcbcb7ea69c31d68b03b852d37)
2025-07-02 13:17:23 -07:00
kassens cd476cf847 Remove now dead argument from resolveClassComponentProps (#33682)
No longer used after https://github.com/facebook/react/pull/33648

DiffTrain build for [73aa744b70](https://github.com/facebook/react/commit/73aa744b7029556430f409ec3887a714940698ba)
2025-07-02 07:53:50 -07:00
kassens 6784e4efd6 Cleanup disableDefaultPropsExceptForClasses flag (#33648)
DiffTrain build for [602917c8cb](https://github.com/facebook/react/commit/602917c8cb521e6f9b8eae7070985e2a698fc0d0)
2025-07-01 12:59:32 -07:00
hoxyq a41d5941e6 fix: rename bottom stack frame (#33680)
`react-stack-bottom-frame` -> `react_stack_bottom_frame`.

This survives `@babel/plugin-transform-function-name`, but now frames
will be displayed as `at Object.react_stack_bottom_frame (...)` in V8.
Checks that were relying on exact function name match were updated to
use either `.indexOf()` or `.includes()`

For backwards compatibility, both React DevTools and Flight Client will
look for both options. I am not so sure about the latter and if React
version is locked.

DiffTrain build for [91d097b2c5](https://github.com/facebook/react/commit/91d097b2c588a0977a7a10ed12512dc8a34e3a5b)
2025-07-01 10:13:11 -07:00
sebmarkbage 7c4226a83a Special case printing Promises in Performance Track Properties (#33670)
Before:
<img width="266" alt="Screenshot 2025-06-30 at 8 32 23 AM"
src="https://github.com/user-attachments/assets/98aae5e1-4b2c-49bd-9b71-040b788c36ba"
/>

After:
<img width="342" alt="Screenshot 2025-06-30 at 8 39 17 AM"
src="https://github.com/user-attachments/assets/cd91c4a6-f6ae-4bec-9cd9-f42f4af468fe"
/>

DiffTrain build for [e9cab42ece](https://github.com/facebook/react/commit/e9cab42ece435ac3478ec85847e352177e596ae0)
2025-06-30 06:28:07 -07:00
sebmarkbage fd62043fd8 Include Component Props in Performance Track (#33655)
Similar to how we can include a Promise resolved value we can include
Component Props.

For now I left out props for Client Components for perf unless they
error. I'll try it for Client Components in general in a separate PR.

<img width="730" alt="Screenshot 2025-06-26 at 5 54 29 PM"
src="https://github.com/user-attachments/assets/f0c86911-2899-4b5f-b45f-5326bdbc630f"
/>
<img width="762" alt="Screenshot 2025-06-26 at 5 54 12 PM"
src="https://github.com/user-attachments/assets/97540d19-5950-4346-99e6-066af086040e"
/>

DiffTrain build for [d2a288febf](https://github.com/facebook/react/commit/d2a288febf61a1755b78ce98b3cb17dd412b81e3)
2025-06-27 05:51:37 -07:00
sebmarkbage 562aaeb636 [Flight] Log aborted await and component renders (#33641)
<img width="926" alt="Screenshot 2025-06-25 at 1 02 14 PM"
src="https://github.com/user-attachments/assets/1877d13d-5259-4cc4-8f48-12981e3073fe"
/>

The I/O entry doesn't show as aborted in the Server Request track
because technically it wasn't. The end time is just made up. It's still
going. It's not aborted until the abort signal propagates and if we do
get that signal wired up before it emits, it instead would show up as
rejected.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>

DiffTrain build for [bb6c9d521e](https://github.com/facebook/react/commit/bb6c9d521e7f44eb9a9754a14cde62c8e6439e5e)
2025-06-25 13:39:33 -07:00
josephsavona 1796cb9a66 [compiler] Fix bug with reassigning function param in destructuring (#33624)
Closes #33577, a bug with ExtractScopeDeclarationsFromDestructuring and
codegen when a function param is reassigned.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33624).
* #33643
* #33642
* #33640
* #33625
* __->__ #33624

DiffTrain build for [9894c488e0](https://github.com/facebook/react/commit/9894c488e0d9a4d9759d80ba8666d4d094b894e9)
2025-06-25 11:24:29 -07:00
josephsavona 59b07a2562 [compiler] Consolidate HIRFunction return information (#33640)
We now have `HIRFunction.returns: Place` as well as `returnType: Type`.
I want to add additional return information, so as a first step i'm
consolidating everything under an object at `HIRFunction.returns:
{place: Place}`. We use the type of this place as the return type. Next
step is to add more properties to this object to represent things like
the return kind.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33640).
* #33643
* #33642
* __->__ #33640
* #33625
* #33624

DiffTrain build for [123ff13b19](https://github.com/facebook/react/commit/123ff13b193cd361a61b99056dd08f2decd7f55d)
2025-06-25 11:18:30 -07:00
josephsavona ab9cbb66a5 [compiler] Avoid empty switch cases (#33625)
Small cosmetic win, found this when i was looking at some code
internally with lots of cases that all share the same logic. Previously,
all the but last one would have an empty block.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33625).
* #33643
* #33642
* #33640
* __->__ #33625
* #33624

DiffTrain build for [e130c08b06](https://github.com/facebook/react/commit/e130c08b06470b5fc4ec8095310d19e782924427)
2025-06-25 11:16:30 -07:00
sebmarkbage 608544bc6f [Fizz] Push a stalled await from debug info to the ownerStack/debugTask (#33634)
If an aborted task is not rendering, then this is an async abort.
Conceptually it's as if the abort happened inside the async gap. The
abort reason's stack frame won't have that on the stack so instead we
use the owner stack and debug task of any halted async debug info.

One thing that's a bit awkward is that if you do have a sync abort and
you use that error as the "reason" then that thing still has a sync
stack in a different component. In another approach I was exploring
having different error objects for each component but I don't think
that's worth it.

DiffTrain build for [cee7939b00](https://github.com/facebook/react/commit/cee7939b0017ff58230e19663c22393bfd9025ef)
2025-06-25 08:20:39 -07:00
sebmarkbage 3d92b7b028 [Flight] Emit Partial Debug Info if we have any at the point of aborting a render (#33632)
When we abort a render we don't really have much information about the
task that was aborted. Because before a Promise resolves there's no
indication about would have resolved it. In particular we don't know
which I/O would've ultimately called resolve().

However, we can at least emit any information we do have at the point
where we emit it. At the least the stack of the top most Promise.

Currently we synchronously flush at the end of an `abort()` but we
should ideally schedule the flush in a macrotask and emit this debug
information right before that. That way we would give an opportunity for
any `cacheSignal()` abort to trigger rejections all the way up and those
rejections informs the awaited stack.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>

DiffTrain build for [e67b4fe22e](https://github.com/facebook/react/commit/e67b4fe22e0c3c267303ee6737aec1db48055022)
2025-06-24 13:43:22 -07:00
josephsavona 57e4b48f33 [compiler] New inference repros/fixes (#33584)
Substantially improves the last major known issue with the new inference
model's implementation: inferring effects of function expressions. I
knowingly used a really simple (dumb) approach in
InferFunctionExpressionAliasingEffects but it worked surprisingly well
on a ton of code. However, investigating during the sync I saw that we
the algorithm was literally running out of memory, or crashing from
arrays that exceeded the maximum capacity. We were accumluating data
flow in a way that could lead to lists of data flow captures compounding
on themselves and growing very large very quickly. Plus, we were
incorrectly recording some data flow, leading to cases where we reported
false positive "can't mutate frozen value" for example.

So I went back to the drawing board. InferMutationAliasingRanges already
builds up a data flow graph which it uses to figure out what values
would be affected by mutations of other values, and update mutable
ranges. Well, the key question that we really want to answer for
inferring a function expression's aliasing effects is which values
alias/capture where. Per the docs I wrote up, we only have to record
such aliasing _if they are observable via mutations_. So, lightbulb:
simulate mutations of the params, free variables, and return of the
function expression and see which params/free-vars would be affected!
That's what we do now, giving us precise information about which such
values alias/capture where. When the "into" is a param/context-var we
use Capture, iwhen the destination is the return we use Alias to be
conservative.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33584).
* #33626
* #33625
* #33624
* __->__ #33584

DiffTrain build for [94cf60bede](https://github.com/facebook/react/commit/94cf60bede7cd6685e07a4374d1e3aa90445130b)
2025-06-24 10:08:41 -07:00
rickhanlonii 7ac4ad8f57 [refactor] remove unused fiberstack functions (#33623)
DiffTrain build for [12eaef7ef5](https://github.com/facebook/react/commit/12eaef7ef5fbf6c9d7ec6e16a04bc207a1a68b91)
2025-06-23 19:45:14 -07:00
sebmarkbage db525a2449 [Flight] Support classes in renderDebugModel (#33590)
This adds better support for serializing class instances as Debug
values.

It adds a new marker on the object `{ "": "$P...", ... }` which
indicates which constructor's prototype to use for this object's
prototype. It doesn't encode arbitrary prototypes and it doesn't encode
any of the properties on the prototype. It might get some of the
properties from the prototype by virtue of `toString` on a `class`
constructor will include the whole class's body.

This will ensure that the instance gets the right name in logs.

Additionally, this now also invokes getters if they're enumerable on the
prototype. This lets us reify values that can only be read from native
classes.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>

DiffTrain build for [18ee505e77](https://github.com/facebook/react/commit/18ee505e7791f2bb55f0e520667c51588df7ba48)
2025-06-22 15:05:47 -07:00
eps1lon 60c8b5b1b9 [Fizz] Ignore error if content node is gone before reveal (#33531)
DiffTrain build for [06e89951be](https://github.com/facebook/react/commit/06e89951be5b4b23ca343d02721521fe392e94c5)
2025-06-20 05:27:54 -07:00
eps1lon e717fc3dad [Fizz] Clean up the replay nodes if we're already rendered past an element (#33581)
DiffTrain build for [79d9aed7ed](https://github.com/facebook/react/commit/79d9aed7edb52e89b3ef9ba3d6b480b04180b664)
2025-06-20 00:34:02 -07:00
eps1lon 2b1f8fbbf1 Fix CI (#33578)
DiffTrain build for [a947eba4f2](https://github.com/facebook/react/commit/a947eba4f2c8741d2c61a3b33fd79cf13bf9f39d)
2025-06-19 14:50:52 -07:00
sebmarkbage a9140c79a9 Expose cacheSignal() alongside cache() (#33557)
This was really meant to be there from the beginning. A `cache()`:ed
entry has a life time. On the server this ends when the render finishes.
On the client this ends when the cache of that scope gets refreshed.

When a cache is no longer needed, it should be possible to abort any
outstanding network requests or other resources. That's what
`cacheSignal()` gives you. It returns an `AbortSignal` which aborts when
the cache lifetime is done based on the same execution scope as a
`cache()`ed function - i.e. `AsyncLocalStorage` on the server or the
render scope on the client.

```js
import {cacheSignal} from 'react';
async function Component() {
  await fetch(url, { signal: cacheSignal() });
}
```

For `fetch` in particular, a patch should really just do this
automatically for you. But it's useful for other resources like database
connections.

Another reason it's useful to have a `cacheSignal()` is to ignore any
errors that might have triggered from the act of being aborted. This is
just a general useful JavaScript pattern if you have access to a signal:

```js
async function getData(id, signal) {
  try {
     await queryDatabase(id, { signal });
  } catch (x) {
     if (!signal.aborted) {
       logError(x); // only log if it's a real error and not due to cancellation
     }
     return null;
  }
}
```

This just gets you a convenient way to get to it without drilling
through so a more idiomatic code in React might look something like.

```js
import {cacheSignal} from "react";

async function getData(id) {
  try {
     await queryDatabase(id);
  } catch (x) {
     if (!cacheSignal()?.aborted) {
       logError(x);
     }
     return null;
  }
}
```

If it's called outside of a React render, we normally treat any cached
functions as uncached. They're not an error call. They can still load
data. It's just not cached. This is not like an aborted signal because
then you couldn't issue any requests. It's also not like an infinite
abort signal because it's not actually cached forever. Therefore,
`cacheSignal()` returns `null` when called outside of a React render
scope.

Notably the `signal` option passed to `renderToReadableStream` in both
SSR (Fizz) and RSC (Flight Server) is not the same instance that comes
out of `cacheSignal()`. If you abort the `signal` passed in, then the
`cacheSignal()` is also aborted with the same reason. However, the
`cacheSignal()` can also get aborted if the render completes
successfully or fatally errors during render - allowing any outstanding
work that wasn't used to clean up. In the future we might also expand on
this to give different
[`TaskSignal`](https://developer.mozilla.org/en-US/docs/Web/API/TaskSignal)
to different scopes to pass different render or network priorities.

On the client version of `"react"` this exposes a noop (both for
Fiber/Fizz) due to `disableClientCache` flag but it's exposed so that
you can write shared code.

DiffTrain build for [e1dc03492e](https://github.com/facebook/react/commit/e1dc03492eedaec517e14a6e32b8fda571d00767)
2025-06-17 14:10:14 -07:00
jbrown215 7709cf1986 [compiler] Add repro for IIFE in ternary causing a bailout (#33546)
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33546).
* #33548
* __->__ #33546

DiffTrain build for [75e78d243f](https://github.com/facebook/react/commit/75e78d243f749d009fa1c5c09c3464301b992718)
2025-06-16 19:05:31 -07:00
jbrown215 99e2e1c37d [compiler] Do not inline IIFEs in value blocks (#33548)
As discussed in chat, this is a simple fix to stop introducing labels
inside expressions.

The useMemo-with-optional test was added in
https://github.com/facebook/react/commit/d70b2c2c4e85c2a7061214c15a8ff13167d10422
and crashes for the same reason- an unexpected label as a value block
terminal.

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

DiffTrain build for [90bee81902](https://github.com/facebook/react/commit/90bee819028bfecb724df298da798607b6a76abf)
2025-06-16 19:01:04 -07:00
kassens bbf2038610 Remove feature flag enableDO_NOT_USE_disableStrictPassiveEffect (#33524)
DiffTrain build for [5d24c64cc9](https://github.com/facebook/react/commit/5d24c64cc9c019fc644c4c6f0da640131b80ba18)
2025-06-16 09:31:58 -07:00
eps1lon 5f92c9a1cd [Fizz] Delay detachment of completed boundaries until reveal (#33511)
DiffTrain build for [a00ca6f6b5](https://github.com/facebook/react/commit/a00ca6f6b51e46a0ccec54a2231bfe7a1ed9ae1d)
2025-06-11 12:30:38 -07:00
kassens 5cd292191e Stringify context as SomeContext instead of SomeContext.Provider (#33507)
This matches the change in React 19 to use `<SomeContext>` as the
preferred way to provide a context.

DiffTrain build for [b7e2de632b](https://github.com/facebook/react/commit/b7e2de632b2a160bc09edda1fbb9b8f85a6914e8)
2025-06-11 09:14:17 -07:00
kassens 54e2e349c4 Remove feature flag enableRenderableContext (#33505)
The flag is fully rolled out.

DiffTrain build for [6c86e56a0f](https://github.com/facebook/react/commit/6c86e56a0fa3c8f253da133330cd5b7d1d20e7e5)
2025-06-11 08:59:22 -07:00
sebmarkbage 4ec413b128 [Fiber] Fix hydration of useId in SuspenseList (#33491)
Includes #31412.

The issue is that `pushTreeFork` stores some global state when reconcile
children. This gets popped by `popTreeContext` in `completeWork`.
Normally `completeWork` returns its own `Fiber` again if it wants to do
a second pass which will call `pushTreeFork` again in the next pass.
However, `SuspenseList` doesn't return itself, it returns the next child
to work on.

The fix is to keep track of the count and push it again it when we
return the next child to attempt.

There are still some outstanding issues with hydration. Like the
backwards test still has the wrong behavior in it because it hydrates
backwards and so it picks up the DOM nodes in reverse order.
`tail="hidden"` also doesn't work correctly.

There's also another issue with `useId` and `AsyncIterable` in
SuspenseList when there's an unknown number of children. We don't
support those showing one at a time yet though so it's not an issue yet.
To fix it we need to add variable total count to the `useId` algorithm.
E.g. by falling back to varint encoding.

---------

Co-authored-by: Rick Hanlon <rickhanlonii@fb.com>
Co-authored-by: Ricky <rickhanlonii@gmail.com>

DiffTrain build for [c38e268978](https://github.com/facebook/react/commit/c38e26897848374c34ac6b651fce4a9088ed4dd0)
2025-06-09 16:46:45 -07:00
jbrown215 fc1c5a2f2c [compiler] Don't include useEffectEvent values in autodeps (#33450)
Summary: useEffectEvent values are not meant to be added to the dep
array

DiffTrain build for [4df098c4c2](https://github.com/facebook/react/commit/4df098c4c2c51a033592ebc84abc47cc49a6bfb2)
2025-06-09 06:37:31 -07:00
eps1lon 42b6956631 [Fizz] Include unit of threshold in rel=expect deopt error (#33476)
DiffTrain build for [b3d5e90786](https://github.com/facebook/react/commit/b3d5e9078685c000e7e9ee3668a7a4b4f3256b1f)
2025-06-06 17:18:31 -07:00
gnoff 4e686834ed [Fizz] Support deeply nested Suspense inside fallback (#33467)
When deeply nested Suspense boundaries inside a fallback of another
boundary resolve it is possible to encounter situations where you either
attempt to flush an aborted Segment or you have a boundary without any
root segment. We intended for both of these conditions to be impossible
to arrive at legitimately however it turns out in this situation you
can. The fix is two-fold

1. allow flushing aborted segments by simply skipping them. This does
remove some protection against future misconfiguraiton of React because
it is no longer an invariant that you hsould never attempt to flush an
aborted segment but there are legitimate cases where this can come up
and simply omitting the segment is fine b/c we know that the user will
never observe this. A semantically better solution would be to avoid
flushing boudaries inside an unneeded fallback but to do this we would
need to track all boundaries inside a fallback or create back pointers
which add to memory overhead and possibly make GC harder to do
efficiently. By flushing extra we're maintaining status quo and only
suffer in performance not with broken semantics.

2. when queuing completed segments allow for queueing aborted segments
and if we are eliding the enqueued segment allow for child segments that
are errored to be enqueued too. This will mean that we can maintain the
invariant that a boundary must have a root segment the first time we
flush it, it just might be aborted (see point 1 above).

This change has two seemingly similar test cases to exercise this fix.
The reason we need both is that when you have empty segments you hit
different code paths within Fizz and so each one (without this fix)
triggers a different error pathway.

This change also includes a fix to our tests where we were not
appropriately setting CSPnonce back to null at the start of each test so
in some contexts scripts would not run for some tests

DiffTrain build for [142aa0744d](https://github.com/facebook/react/commit/142aa0744d0e73dc5390bc19d4d41dd8aeda2b19)
2025-06-06 12:05:45 -07:00
sebmarkbage fa26265844 [Fizz] Shorten throttle to hit a specific target metric (#33463)
Adding throttling or delaying on images, can obviously impact metrics.
However, it's all in the name of better actual user experience overall.
(Note that it's not strictly worse even for metric. Often it's actually
strictly better due to less work being done overall thanks to batching.)

Metrics can impact things like search ranking but I believe this is on a
curve. If you're already pretty good, then a slight delay won't suddenly
make you rank in a completely different category. Similarly, if you're
already pretty bad then a slight delay won't make it suddenly way worse.
It's still in the same realm. It's just one weight of many. I don't
think this will make a meaningful practical impact and if it does,
that's probably a bug in the weights that will get fixed.

However, because there's a race to try to "make everything green" in
terms of web vitals, if you go from green to yellow only because of some
throttling or suspensey images, it can feel bad. Therefore this
implements a heuristic where if the only reason we'd miss a specific
target is because of throttling or suspensey images, then we shorten the
timeout to hit the metric. This is a worse user experience because it
can lead to extra flashing but feeling good about "green" matters too.

If you then have another reveal that happens to be the largest
contentful paint after that, then that's throttled again so that it
doesn't become flashy after that. If you've already missed the deadline
then you're not going to hit your metric target anyway. It can affect
average but not median.

This is mainly about LCP. It doesn't affect FCP since that doesn't have
a throttle. If your LCP is the same as your FCP then it also doesn't
matter.

We assume that `performance.now()`'s zero point starts at the "start of
the navigation" which makes this simple. Even if we used the
`PerformanceNavigationTiming` API it would just tell us the same thing.

This only implements for Fizz since these metrics tend to currently only
by tracked for initial loads, but with soft navs tracking we could
consider implementing the same for Fiber throttles.

DiffTrain build for [6ccf328499](https://github.com/facebook/react/commit/6ccf328499f06c140ffe96a096744c22319394cc)
2025-06-06 11:09:11 -07:00
sebmarkbage 7cc8093003 [Fizz] Error and deopt from rel=expect for large documents without boundaries (#33454)
We want to make sure that we can block the reveal of a well designed
complete shell reliably. In the Suspense model, client transitions don't
have any way to implicitly resolve. This means you need to use Suspense
or SuspenseList to explicitly split the document. Relying on implicit
would mean you can't add a Suspense boundary later where needed. So we
highly encourage the use of them around large content.

However, if you have constructed a too large shell (e.g. by not adding
any Suspense boundaries at all) then that might take too long to render
on the client. We shouldn't punish users (or overzealous metrics
tracking tools like search engines) in that scenario.

This opts out of render blocking if the shell ends up too large to be
intentional and too slow to load. Instead it deopts to showing the
content split up in arbitrary ways (browser default). It only does this
for SSR, and not client navs so it's not reliable.

In fact, we issue an error to `onError`. This error is recoverable in
that the document is still produced. It's up to your framework to decide
if this errors the build or just surface it for action later.

What should be the limit though? There's a trade off here. If this limit
is too low then you can't fit a reasonably well built UI within it
without getting errors. If it's too high then things that accidentally
fall below it might take too long to load.

I came up with 512kB of uncompressed shell HTML. See the comment in code
for the rationale for this number. TL;DR: Data and theory indicates that
having this much content inside `rel="expect"` doesn't meaningfully
change metrics. Research of above-the-fold content on various websites
indicate that this can comfortable fit all of them which should be
enough for any intentional initial paint.

DiffTrain build for [d177272802](https://github.com/facebook/react/commit/d177272802b7f86a847312c23b7e60a6f56434de)
2025-06-06 07:36:11 -07:00
sebmarkbage 57de70340f [Fizz] Suspensey Images for View Transition Reveals (#33433)
Block the view transition on suspensey images Up to 500ms just like the
client.

We can't use `decode()` because a bug in Chrome where those are blocked
on `startViewTransition` finishing we instead rely on sync decoding but
also that the image is live when it's animating in and we assume it
doesn't start visible.

However, we can block the View Transition from starting on the `"load"`
or `"error"` events.

The nice thing about blocking inside `startViewTransition` is that we
have already done the layout so we can only wait on images that are
within the viewport at this point. We might want to do that in Fiber
too. If many image doesn't have fixed size but need to load first, they
can all end up in the viewport. We might consider only doing this for
images that have a fixed size or only a max number that doesn't have a
fixed size.

DiffTrain build for [22b929156c](https://github.com/facebook/react/commit/22b929156c325eaf52c375f0c62801831951814a)
2025-06-06 07:21:34 -07:00
yungsters 5dd497f11e Enable the enableEagerAlternateStateNodeCleanup Feature Flag (#33447)
## Summary

Enables the `enableEagerAlternateStateNodeCleanup` feature flag for all
variants, while maintaining the `__VARIANT__` for the internal React
Native flavor for backtesting reasons.

## How did you test this change?

```
$ yarn test
```

DiffTrain build for [dddcae7a11](https://github.com/facebook/react/commit/dddcae7a11b8241cbd6e2de55f9e68881baea458)
2025-06-05 14:30:33 -07:00
sebmarkbage 6c6f461f22 Replace Implicit Options on SuspenseList with Explicit Options (#33424)
We want to change the defaults for `revealOrder` and `tail` on
SuspenseList. This is an intermediate step to allow experimental users
to upgrade.

To explicitly specify these options I added `revealOrder="independent"`
and `tail="visible"`.

I then added warnings if `undefined` or `null` is passed. You must now
always explicitly specify them. However, semantics are still preserved
for now until the next step.

We also want to change the rendering order of the `children` prop for
`revealOrder="backwards"`. As an intermediate step I first added
`revealOrder="unstable_legacy-backwards"` option. This will only be
temporary until all users can switch to the new `"backwards"` semantics
once we flip it in the next step.

I also clarified the types that the directional props requires iterable
children but not iterable inside of those. Rows with multiple items can
be modeled as explicit fragments.

DiffTrain build for [d742611ce4](https://github.com/facebook/react/commit/d742611ce40545127032f4e221c78bf9f70eb437)
2025-06-03 14:47:47 -07:00
sebmarkbage 45da59b429 Use underscore instead of « » for useId algorithm (#33422)
Alternative to #33421. The difference is that this also adds an
underscore between the "R" and the ID.

The reason we wanted to use special characters is because we use the
full spectrum of A-Z 0-9 in our ID generation so we can basically
collide with any common word (or anyone using a similar algorithm,
base64 or even base16). It's a little less likely that someone would put
`_R_` specifically unless you generate like two IDs separated by
underscore.

![9w2ogt](https://github.com/user-attachments/assets/21b2d2ac-1a3a-4657-ba0b-1616e49dfdee)

DiffTrain build for [1ae0a845bd](https://github.com/facebook/react/commit/1ae0a845bde5b95dfc319cadf366cb7b3fb1ca92)
2025-06-03 08:37:35 -07:00
eps1lon 3fd524e9f5 [Fizz] Add Owner Stacks when render is aborted (#32735)
DiffTrain build for [4a1f29079c](https://github.com/facebook/react/commit/4a1f29079ccc61659e026bbcf205bc8d53780927)
2025-06-02 10:33:56 -07:00
mofeiZ e0d5f7404d [compiler][patch] Emit unary expressions instead of negative numbers (#33383)
This is a babel bug + edge case.

Babel compact mode produces invalid JavaScript (i.e. parse error) when
given a `NumericLiteral` with a negative value.

See https://codesandbox.io/p/devbox/5d47fr for repro.

DiffTrain build for [526dd340b3](https://github.com/facebook/react/commit/526dd340b3e77193846fe5eed02b9bb89d7c2d15)
2025-06-02 08:53:19 -07:00
gnoff 457e990f59 Allow nonce to be used on hoistable styles (#32461)
fixes https://github.com/facebook/react/issues/32449

This is my first time touching this code. There are multiple systems in
place here and I wouldn't be surprised to learn that this has to be
handled in some other areas too. I have found some other style-related
code areas but I had no time yet to double-check them.

cc @gnoff

DiffTrain build for [14094f80cb](https://github.com/facebook/react/commit/14094f80cbf52cad4024211080d8491f3334ef61)
2025-05-29 08:23:36 -07:00
eps1lon 6af6cff000 [react-dom] Enforce small gap between completed navigation and default Transition indicator (#33354)
DiffTrain build for [5717f1933f](https://github.com/facebook/react/commit/5717f1933f2e8b10406fde1043c3047cbfbddc82)
2025-05-28 10:52:45 -07:00
kassens 7f8efe8430 Revert "enableViewTransition in www" (#33362)
We need to do some more testing here.

Reverts facebook/react#33357

DiffTrain build for [283f87f083](https://github.com/facebook/react/commit/283f87f083f285c060582e1096cebed794d4b748)
2025-05-27 14:25:18 -07:00
mofeiZ cb7061578a [compiler][gating] Custom opt out directives (experimental option) (#33328)
Adding an experimental / unstable compiler config to enable custom
opt-out directives

DiffTrain build for [f9ae0a4c2e](https://github.com/facebook/react/commit/f9ae0a4c2edc9ad93507b550f2aeb60119955336)
2025-05-27 09:08:44 -07:00
kassens 4dbb17c404 [fb-www] ship enableViewTransition (#33357)
DiffTrain build for [f702620cea](https://github.com/facebook/react/commit/f702620cea32f59a101d4f60fbff7a7495df76f7)
2025-05-27 08:30:45 -07:00
sebmarkbage 751d4c1ee2 [Fizz] Block on Suspensey Fonts during reveal (#33342)
This is the same technique we do for the client except we don't check
whether this is newly created font loading to keep code small.

Unfortunately, we can't use this technique for Suspensey images. They'll
need to block before we call `startViewTransition` in a separate
refactor. This is due to a bug in Chrome where `img.decode()` doesn't
resolve until `startViewTransition` does.

DiffTrain build for [c0464aedb1](https://github.com/facebook/react/commit/c0464aedb16b1c970d717651bba8d1c66c578729)
2025-05-23 10:31:54 -07:00