6392 Commits
Author SHA1 Message Date
Sebastian MarkbågeandGitHub 21fdf308a1 Use a shared noop function from shared/noop (#33154)
Stacked on #33150.

We use `noop` functions in a lot of places as place holders. I don't
think there's any real optimizations we get from having separate
instances. This moves them to use a common instance in `shared/noop`.
2025-05-08 21:33:18 -04:00
Jack PopeandGitHub 4ca97e4891 Clean up enableSiblingPrerendering flag (#32319) 2025-05-08 20:49:23 -04:00
Sebastian MarkbågeandGitHub 9b79292ae7 Add plumbing for onDefaultTransitionIndicator (#33150)
This just adds the options at the root and wire it up to the root but it
doesn't do anything yet.
2025-05-08 20:42:50 -04:00
Dawid MałeckiandGitHub 9518f11856 Root import types from react-native in ReactNativeTypes (#33063) 2025-05-08 12:12:35 +01:00
Ruslan LesiutinandGitHub 557a64795c React DevTools 6.1.1 -> 6.1.2 (#33142)
Patch release to mitigate https://github.com/facebook/react/issues/32659

Essentially just 6.1.1 with:
* Restore all Transitions for Tree updates
([eps1lon](https://github.com/eps1lon) in
[#33042](https://github.com/facebook/react/pull/33042))
* Restore "double-click to view owners tree" functionality
([eps1lon](https://github.com/eps1lon) in
[#33039](https://github.com/facebook/react/pull/33039))
2025-05-08 08:01:17 +01:00
Jack PopeandGitHub 8a8df5dbdd Add dispatchEvent to fragment instances (#32813)
`fragmentInstance.dispatchEvent(evt)` calls `element.dispatchEvent(evt)`
on the fragment's host parent. This mimics bubbling if the
`fragmentInstance` could receive an event itself.

If the parent is disconnected, there is a dev warning and no event is
dispatched.
2025-05-07 14:00:59 -04:00
Sebastian MarkbågeandGitHub a437c99ff7 [Flight] Clarify that location field is a FunctionLocation not a CallSite (#33141)
Follow up to #33136.

This clarifies in the types where the conversion happens from a CallSite
which we use to simulate getting the enclosing line/col to a
FunctionLocation which doesn't represent a CallSite but actually just
the function which only has an enclosing line/col.
2025-05-07 13:02:41 -04:00
Jack PopeandGitHub 4206fe4982 Allow fragment refs to attempt focus/focusLast on nested host children (#33058)
This enables `focus` and `focusLast` methods on FragmentInstances to
search nested host components, depth first. Attempts focus on each child
and bails if one is successful. Previously, only the first level of host
children would attempt focus.

Now if we have an example like

```
component MenuItem() {
  return (<div><a>{...}</a></div>)
}

component Menu() {
  return <Fragment>{items.map(i => <MenuItem i={i} />)}</Fragment>
}
```
We can target focus on the first or last a tag, rather than checking
each wrapping div and then noop.
2025-05-07 12:47:28 -04:00
Sebastian MarkbågeandGitHub 4a702865dd [Flight] Encode enclosing line/column numbers and use it to align the fake function (#33136)
Stacked on #33135.

This encodes the line/column of the enclosing function as part of the
stack traces. When that information is available.

I adjusted the fake function code generation so that the beginning of
the arrow function aligns with these as much as possible.

This ensures that when the browser tries to look up the line/column of
the enclosing function, such as for getting the function name, it gets
the right one. If we can't get the enclosing line/column, then we encode
it at the beginning of the file. This is likely to get a miss in the
source map identifiers, which means that the function name gets
extracted from the runtime name instead which is better.

Another thing where this is used is the in the Performance Track.
Ideally that would be fixed by
https://issues.chromium.org/u/1/issues/415968771 but the enclosing
information is useful for other things like the function name resolution
anyway.

We can also use this for the "View source for this element" in React
DevTools.
2025-05-07 12:34:55 -04:00
Sebastian MarkbågeandGitHub 0ff1d13b80 [Flight] Parse Stack Trace from Structured CallSite if available (#33135)
This is first step to include more enclosing line/column in the parsed
data.

We install our own `prepareStackTrace` to collect structured callsite
data and only fall back to parsing the string if it was already
evaluated or if `prepareStackTrace` doesn't work in this environment.

We still mirror the default V8 format for encoding the function name
part. A lot of this is covered by tests already.
2025-05-07 11:43:37 -04:00
YongSeok Jang (장용석)andGitHub 53c9f81049 [DevTools] Use Popover API for TraceUpdates highlighting (#32614)
## Summary

When using React DevTools to highlight component updates, the highlights
would sometimes appear behind elements that use the browser's
[top-layer](https://developer.mozilla.org/en-US/docs/Glossary/Top_layer)
(such as `<dialog>` elements or components using the Popover API). This
made it difficult to see which components were updating when they were
inside or behind top-layer elements.

This PR fixes the issue by using the Popover API to ensure that
highlighting appears on top of all content, including elements in the
top-layer. The implementation maintains backward compatibility with
browsers that don't support the Popover API.

## How did you test this change?

I tested this change in the following ways:

1. Manually tested in Chrome (which supports the Popover API) with:
- Created a test application with React components inside `<dialog>`
elements and custom elements using the Popover API
- Verified that component highlighting appears above these elements when
they update
- Confirmed that highlighting displays correctly for nested components
within top-layer elements

2. Verified backward compatibility:
- Tested in browsers without Popover API support to ensure fallback
behavior works correctly
- Confirmed that no errors occur and highlighting still functions as
before

3. Ran the React DevTools test suite:
   - All tests pass successfully
   - No regressions were introduced

[demo-page](https://devtools-toplayer-demo.vercel.app/)
[demo-repo](https://github.com/yongsk0066/devtools-toplayer-demo)

### AS-IS

https://github.com/user-attachments/assets/dc2e1281-969f-4f61-82c3-480153916969

### TO-BE

https://github.com/user-attachments/assets/dd52ce35-816c-42f0-819b-0d5d0a8a21e5
2025-05-07 15:48:17 +01:00
Jack PopeandGitHub e5a8de81e5 Add compareDocumentPosition to fragment instances (#32722)
This adds `compareDocumentPosition(otherNode)` to fragment instances.

The semantics implemented are meant to match typical element
positioning, with some fragment specifics. See the unit tests for all
expectations.

- An element preceding a fragment is `Node.DOCUMENT_POSITION_PRECEDING`
- An element after a fragment is `Node.DOCUMENT_POSITION_FOLLOWING`
- An element containing the fragment is
`Node.DOCUMENT_POSITION_PRECEDING` and
`Node.DOCUMENT_POSITION_CONTAINING`
- An element within the fragment is
`Node.DOCUMENT_POSITION_CONTAINED_BY`
- An element compared against an empty fragment will result in
`Node.DOCUMENT_POSITION_DISCONNECTED` and
`Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC`

Since we assume a fragment instances target children are DOM siblings
and we want to compare the full fragment as a pseudo container, we can
compare against the first target child outside of handling the special
cases (empty fragments and contained elements).
2025-05-06 13:01:40 -04:00
Sebastian MarkbågeandGitHub 845d93742f Remove useId semantics from View Transition name generation (#33094)
Originally I thought it was important that SSR used the same View
Transition name as the client so that the Fizz runtime could emit those
names and then the client could pick up and take over. However, I no
longer believe that approach is feasible. Instead, the names can be
generated only during that particular animation.

Therefore we can simplify the auto name assignment to not have to
consider the hydration.
2025-05-06 10:33:03 -04:00
Sebastian MarkbågeandGitHub 54a50729cc [Fiber] Replay events between commits (#33130)
Stacked on #33129. Flagged behind `enableHydrationChangeEvent`.

If you type into a controlled input before hydration and something else
rerenders like a setState in an effect, then the controlled input will
reset to whatever React thought it was. Even with event replaying that
this is stacked on, if the second render happens before event replaying
has fired in a separate task.

We don't want to flush inside the commit phase because then things like
flushSync in these events wouldn't work since they're inside the commit
stack.

This flushes all event replaying between renders by flushing it at the
end of `flushSpawned` work. We've already committed at that point and is
about to either do subsequent renders or yield to event loop for passive
effects which could have these events fired anyway. This just ensures
that they've already happened by the time subsequent renders fire. This
means that there's now a type of event that fire between sync render
passes.
2025-05-06 00:23:27 -04:00
Sebastian MarkbågeandGitHub 587cb8f896 [Fiber] Replay onChange Events if input/textarea/select has changed before hydration (#33129)
This fixes a long standing issue that controlled inputs gets out of sync
with the browser state if it's changed before we hydrate.

This resolves the issue by replaying the change events (click, input and
change) if the value has changed by the time we commit the hydration.
That way you can reflect the new value in state to bring it in sync. It
does this whether controlled or uncontrolled.

The idea is that this should be ok to replay because it's similar to the
continuous events in that it doesn't replay a sequence but only reflects
the current state of the tree.

Since this is a breaking change I added it behind
`enableHydrationChangeEvent` flag.

There is still an additional issue remaining that I intend to address in
a follow up. If a `useLayoutEffect` triggers an sync rerender on
hydration (always a bad idea) then that can rerender before we have had
a chance to replay the change events. If that renders through a input
then that input will always override the browser value with the
controlled value. Which will reset it before we've had a change to
update to the new value.
2025-05-06 00:10:05 -04:00
Matt CarrollandGitHub 79586c7eb6 Add test for multiple form submissions (#33059)
Test for #30041 and #33055
2025-05-05 14:47:47 -07:00
Jack PopeandGitHub edf550b679 Ship enableFabricCompleteRootInCommitPhase (#33064)
This was shipped internally. Cleaning up the flag.
2025-05-05 13:36:44 -04:00
Sebastian "Sebbie" SilbermannandGitHub b9cfa0d308 [Flight] Prevent serialized size leaking across requests (#33121) 2025-05-05 18:30:33 +02:00
Sebastian MarkbågeandGitHub 52ea641449 [Flight] Don't increase serializedSize for every recursive pass (#33123)
I noticed that we increase this in the recursive part of the algorithm.
This would mean that we'd count a key more than once if it has Server
Components inside it recursively resolving. This moves it out to where
we enter from toJSON. Which is called once per JSON entry (and therefore
once per key).
2025-05-05 11:37:39 -04:00
Stephen ZhouandGitHub 3ec88e797f [eslint-plugin-react-hooks] update doc url for rules of hooks (#33118) 2025-05-05 17:37:06 +02:00
Sebastian "Sebbie" SilbermannandGitHub 0ca8420f9d [Flight] Use valid CSS selectors in useId format (#33099) 2025-05-04 13:47:32 +02:00
Jan KassensandGitHub 9de0304ad7 Add missing copyright header (#33106)
This made the build fail since there was no file header comment.
2025-05-02 16:52:17 -04:00
Jan KassensandGitHub 4c4a57c4f9 [eslint-plugin-react-hooks] updates for component syntax (#33089)
Adds support for Flow's component and hook syntax.
[docs](https://flow.org/en/docs/react/component-syntax/)
2025-05-02 15:04:45 -04:00
Sebastian MarkbågeandGitHub f739642745 [Fizz] Always load the external runtime if one is provided (#33091)
Because we now decided whether to outline in the flushing phase, when
we're writing the preamble we don't yet know if we will make that
decision so we don't know if it's safe to omit the external runtime.

However, if you are providing an external runtime it's probably a pretty
safe bet you're streaming something dynamically that's likely to need it
so we can always include it.

The main thing is that this makes it hard to test it because it affects
our tests in ways it wouldn't otherwise so we have to add a bunch of
conditions.
2025-05-01 18:14:42 -04:00
Sebastian MarkbågeandGitHub 0ed6ceb9f6 [Fizz] Add "Queued" Status to SSR:ed Suspense Boundaries (#33087)
Stacked on #33076.

This fixes a bug where we used the "complete" status but the
DOMContentLoaded event. This checks for not "loading" instead.

We also add a new status where the boundary has been marked as complete
by the server but has not yet flushed either due to being throttled,
suspended on CSS or animating.
2025-05-01 16:11:54 -04:00
Sebastian MarkbågeandGitHub ee7fee8f88 [Fizz] Batch Suspense Boundary Reveal with Throttle (#33076)
Stacked on #33073.

React semantics is that Suspense boundaries reveal with a throttle
(300ms). That helps avoid flashing reveals when a stream reveals many
individual steps back to back. It can also improve overall performance
by batching the layout and paint work that has to happen at each step.

Unfortunately we never implemented this for SSR streaming - only for
client navigations. This is highly noticeable on very dynamic sites with
lots of Suspense boundaries. It can look good with a client nav but feel
glitchy when you reload the page or initial load.

This fixes the Fizz runtime to be throttled and reveals batched into a
single paint at a time. We do this by first tracking the last paint
after the complete (this will be the first paint if `rel="expect"` is
respected). Then in the `completeBoundary` operation we queue the
operation and then flush it all into a throttled batch.

Another motivation is that View Transitions need to operate as a batch
and individual steps get queued in a sequence so it's extra important to
include as much content as possible in each animated step. This will be
done in a follow up for SSR View Transitions.
2025-05-01 16:09:37 -04:00
Sebastian MarkbågeandGitHub ee077b6ccd [Fizz] Don't handle errors in completeBoundary instruction (#33073)
Stacked on #33066 and #33068.

Currently we're passing `errorDigest` to `completeBoundary` if there is
a client side error (only CSS loading atm). This only exists because of
`completeBoundaryWithStyles`. Normally if there's a server-side error
we'd emit the `clientRenderBoundary` instruction instead. This adds
unnecessary code to the common case where all styles are in the head.
This is about to get worse with batching because client render shouldn't
be throttled but complete should be.

The first commit moves the client render logic inline into
`completeBoundaryWithStyles` so we only pay for it when styles are used.

However, the approach I went with in the second commit is to reuse the
`$RX` instruction instead (`clientRenderBoundary`). That way if you have
both it ends up being amortized. However, it does mean we have to emit
the `$RX` (along with the `$RC` helper if any
`completeBoundaryWithStyles` instruction is needed.
2025-05-01 15:44:17 -04:00
Sebastian MarkbågeandGitHub bb57fa7351 [Fizz] Share code between inline and external runtime (#33066)
Stacked on #33065.

The runtime is about to be a lot more complicated so we need to start
sharing some more code.

The problem with sharing code is that we want the inline runtime to as
much as possible be isolated in its scope using only a few global
variables to refer across runtimes.

A problem with Closure Compiler is that it refuses to inline functions
if they have closures inside of them. Which makes sense because of how
VMs work it can cause memory leaks. However, in our cases this doesn't
matter and code size matters more. So we can't use many clever tricks.

So this just favors writing the source in the inline form. Then we add
an extra compiler pass to turn those global variables into local
variables in the external runtime.
2025-05-01 14:25:10 -04:00
Sebastian MarkbågeandGitHub 71797c871b [Fizz] Ignore error if content node is gone (#33068)
We normally expect the segment to exist whatever the client does while
streaming. However, when hydration errors at the root of the shell for a
whole document render, then we clear nodes from body which can include
our segments. We don't need them anymore because we switched to client
rendering.

It triggers an error accessing parent node which can safely be ignored.
This just helps avoid confusion in this scenario.

This also covers up the error in #33067. Which doesn't actually cause
any visible problems other than error logging. However, ideally we
wouldn't emit completeBoundary instructions if the boundary is inside a
cancelled fallback.
2025-04-30 17:51:39 -04:00
Sebastian MarkbågeandGitHub 49ea8bf569 [Flight] Defer Elements if the parent chunk is too large (#33030)
Same principle as #33029 but for Flight.

We pretty aggressively create separate rows for things in Flight (every
Server Component that's an async function create a microtask). However,
sync Server Components and just plain Host Components are not. Plus we
should ideally ideally inline more of the async ones in the same way
Fizz does.

This means that we can create rows that end up very large. Especially if
all the data is already available. We can't show the parent content
until the whole thing loads on the client.

We don't really know where Suspense boundaries are for Flight but any
Element is potentially a point that can be split.

This heuristic counts roughly how much we've serialized to block the
current chunk and once a limit is exceeded, we start deferring all
Elements. That way they get outlined into future chunks that are later
in the stream. Since they get replaced by Lazy references the parent can
potentially get unblocked.

This can help if you're trying to stream a very large document with a
client nav for example.
2025-04-30 14:21:28 -04:00
Sebastian MarkbågeandGitHub 9a52ad9fd9 [Fizz] Remove globals from external runtime (#33065)
We never emit any inline functions when we use external runtime so this
global shouldn't be needed.
2025-04-30 14:21:14 -04:00
Sebastian "Sebbie" SilbermannandGitHub fa8e3a251e [devtools] Restore all Transitions for Tree updates (#33042) 2025-04-30 19:51:40 +02:00
Jack PopeandGitHub 408d055a3b Add Fragment Refs to Fabric with intersection observer support (#33056)
Adds Fragment Ref support to RN through the Fabric config, starting with
`observeUsing`/`unobserveUsing`. This is mostly a copy from the
implementation on DOM, and some of it can likely be shared in the future
but keeping it separate for now and we can refactor as we add more
features.

Added a basic test with Fabric, but testing specific methods requires so
much mocking that it doesn't seem valuable here.

I built Fabric and ran on the Catalyst app internally to test with
intersection observers end to end.
2025-04-30 10:47:18 -04:00
Sebastian "Sebbie" SilbermannandGitHub fbf29ccaa3 [devtools] Restore "double-click to view owners tree" functionality (#33039) 2025-04-30 11:11:33 +02:00
Sebastian MarkbågeandGitHub 62960c67c8 Run Component Track Logs in the console.createTask() of the Fiber (#32809)
Stacked on #32736.

That way you can find the owner stack of each component that rerendered
for context.

In addition to the JSX callsite tasks that we already track, I also
added tracking of the first `setState` call before rendering.

We then run the "Update" entries in that task. That way you can find the
callsite of the first setState and therefore the "cause" of a render
starting by selecting the "Update" track.

Unfortunately this is blocked on bugs in Chrome that makes it so that
these stacks are not reliable in the Performance tab. It basically just
doesn't work.
2025-04-29 22:17:17 -04:00
Sebastian MarkbågeandGitHub cd4e4d7599 Use console.timeStamp instead of performance.measure in Component Performance Track (#32736)
This is a new extension that Chrome added to the existing
`console.timeStamp` similar to the extensions added to
`performance.measure`. This one should be significantly faster because
it doesn't have the extra object indirection, it doesn't return a
`PerformanceMeasure` entry and doesn't register itself with the global
system of entries.

I also use `performance.measure` in DEV for errors since we can attach
the error to the `properties` extension which doesn't exist for
`console.timeStamp`.

A downside of using this API is that there's no programmatic API for the
site itself to collect its own logs from React. Which the previous
allowed us to use the standard `performance.getEntries()` for. The
recommendation instead will be for the site to patch `console.timeStamp`
if it wants to collect measurements from React just like you're
recommended to patch `console.error` or `fetch` or whatever to collect
other instrumentation metrics.

This extension works in Chrome canary but it doesn't yet work fully in
Chrome stable. We might want to wait until it has propagated to Chrome
to stable. It should be in Chrome 136.
2025-04-29 21:40:10 -04:00
Sebastian MarkbågeandGitHub 18212ca960 [Fizz] Outline if a boundary would add too many bytes to the next completion (#33029)
Follow up to #33027.

This enhances the heuristic so that we accumulate the size of the
currently written boundaries. Starting from the size of the root (minus
preamble) for the shell.

This ensures that if you have many small boundaries they don't all
continue to get inlined. For example, you can wrap each paragraph in a
document in a Suspense boundary to regain document streaming
capabilities if that's what you want.

However, one consideration is if it's worth producing a fallback at all.
Maybe if it's like `null` it's free but if it's like a whole alternative
page, then it's not. It's possible to have completely useless Suspense
boundaries such as when you nest several directly inside each other. So
this uses a limit of at least 500 bytes of the content itself for it to
be worth outlining at all. It also can't be too small because then for
example a long list of paragraphs can never be outlined.

In the fixture I straddle this limit so some paragraphs are too small to
be considered. An unfortunate effect of that is that you can end up with
some of them not being outlined which means that they appear out of
order. SuspenseList is supposed to address that but it's unfortunate.

The limit is still fairly high though so it's unlikely that by default
you'd start outlining anything within the viewport at all. I had to
reduce the `progressiveChunkSize` by an order of magnitude in my fixture
to try it out properly.
2025-04-29 19:13:28 -04:00
Sebastian MarkbågeandGitHub 88b9767404 Hack to recover from reading the wrong Fiber (#33055)
`requestFormReset` incorrectly tries to get the current dispatch queue
from the Fiber. However, the Fiber might be the workInProgress which is
an inconsistent state.

This hack just tries the other Fiber if it detects one of the known
inconsistent states but there can be more.

Really we should stash the dispatch queue somewhere stateful which is
effectively what `setState` does by binding it to the closure.
2025-04-29 13:36:19 -04:00
Pieter De BaetsandGitHub 0038c501a3 [react-native] Pull up enableFastAddPropertiesInDiffing check (#33043)
## Summary

We don't need the isArray check for this experiment, as
`fastAddProperties` already does the same. Also renaming
slowAddProperties to make it clearer we can fully remove this codepath
once fastAddProperties is fully rolled out.

## How did you test this change?

```
yarn test packages/react-native-renderer -r=xplat --variant=true
```
2025-04-29 11:10:18 +01:00
Sebastian MarkbågeandGitHub 5dc00d6b2b [Fizz] Reset Instructions on ResumableState (#33046)
When we end up creating an incomplete state in the shell we end up not
flushing anything. As a hack, in this case we need to reset the
ResumableState because some of the ResumableState is still relevant
(e.g. any preloads that went into headers) but some of the
ResumableState needs to be reset since they assume that what we produced
actually flushed.

We didn't reset the instructions state but we haven't actually flushed
any of the instructions so it needs to reset.
2025-04-28 15:50:06 -04:00
Sebastian "Sebbie" SilbermannandGitHub c498bfce8b [devtools] Allow inspecting cause, name, message, stack of Errors in props (#33023) 2025-04-26 07:20:57 +02:00
Sebastian MarkbågeandGitHub 8e9a5fc6c1 [Fizz] Enable the progressiveChunkSize option (#33027)
Since the very beginning we have had the `progressiveChunkSize` option
but we never actually took advantage of it because we didn't count the
bytes that we emitted. This starts counting the bytes by taking a pass
over the added chunks each time a segment completes.

That allows us to outline a Suspense boundary to stream in late even if
it is already loaded by the time that back-pressure flow and in a
`prerender`. Meaning it gets inserted with script.

The effect can be seen in the fixture where if you have large HTML
content that can block initial paint (thanks to
[`rel="expect"`](https://github.com/facebook/react/pull/33016) but also
nested Suspense boundaries). Before this fix, the paint would be blocked
until the large content loaded. This lets us paint the fallback first in
the case that the raw bytes of the content takes a while to download.

You can set it to `Infinity` to opt-out. E.g. if you want to ensure
there's never any scripts. It's always set to `Infinity` in
`renderToHTML` and the legacy `renderToString`.

One downside is that if we might choose to outline a boundary, we need
to let its fallback complete.

We don't currently discount the size of the fallback but really just
consider them additive even though in theory the fallback itself could
also add significant size or even more than the content. It should maybe
really be considered the delta but that would require us to track the
size of the fallback separately which is tricky.

One problem with the current heuristic is that we just consider the size
of the boundary content itself down to the next boundary. If you have a
lot of small boundaries adding up, it'll never kick in. I intend to
address that in a follow up.
2025-04-25 16:10:53 -04:00
Sebastian MarkbågeandGitHub 143d3e1b89 [Fizz] Emit link rel="expect" to block render before the shell has fully loaded (#33016)
The semantics of React is that anything outside of Suspense boundaries
in a transition doesn't display until it has fully unsuspended. With SSR
streaming the intention is to preserve that.

We explicitly don't want to support the mode of document streaming
normally supported by the browser where it can paint content as tags
stream in since that leads to content popping in and thrashing in
unpredictable ways. This should instead be modeled explictly by nested
Suspense boundaries or something like SuspenseList.

After the first shell any nested Suspense boundaries are only revealed,
by script, once they're fully streamed in to the next boundary. So this
is already the case there. However, for the initial shell we have been
at the mercy of browser heuristics for how long it decides to stream
before the first paint.

Chromium now has [an API explicitly for this use
case](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API/Using#stabilizing_page_state_to_make_cross-document_transitions_consistent)
that lets us model the semantics that we want. This is always important
but especially so with MPA View Transitions.

After this a simple document looks like this:

```html
<!DOCTYPE html>
<html>
  <head>
     <link rel="expect" href="#«R»" blocking="render"/>
  </head>
  <body>
    <p>hello world</p>
    <script src="bootstrap.js" id="«R»" async=""></script>
    ...
  </body>
</html>
```

The `rel="expect"` tag indicates that we want to wait to paint until we
have streamed far enough to be able to paint the id `"«R»"` which
indicates the shell.

Ideally this `id` would be assigned to the root most HTML element in the
body. However, this is tricky in our implementation because there can be
multiple and we can render them out of order.

So instead, we assign the id to the first bootstrap script if there is
one since these are always added to the end of the shell. If there isn't
a bootstrap script then we emit an empty `<template
id="«R»"></template>` instead as a marker.

Since we currently put as much as possible in the shell if it's loaded
by the time we render, this can have some negative effects for very
large documents. We should instead apply the heuristic where very large
Suspense boundaries get outlined outside the shell even if they're
immediately available. This means that even prerenders can end up with
script tags.

We only emit the `rel="expect"` if you're rendering a whole document.
I.e. if you rendered either a `<html>` or `<head>` tag. If you're
rendering a partial document, then we don't really know where the
streaming parts are anyway and can't provide such guarantees. This does
apply whether you're streaming or not because we still want to block
rendering until the end, but in practice any serialized state that needs
hydrate should still be embedded after the completion id.
2025-04-25 11:52:28 -04:00
Sebastian MarkbågeandGitHub 693803a9bb Rename Suspense unstable_name to name (#33014)
This was only used by Transition Tracing which isn't really used
anywhere.

However, we want to start using it for other DevTools.
2025-04-24 16:53:34 -04:00
197d6a0403 [devtools] 1st class support of used Thenables (#32989)
Co-authored-by: Ruslan Lesiutin <rdlesyutin@gmail.com>
2025-04-24 13:46:31 +02:00
Hendrik LiebauandGitHub 914319ae59 [Flight] Don't hang forever when prerendering a rejected promise (#32953) 2025-04-23 11:02:43 +02:00
Sebastian MarkbågeandGitHub 3ef31d196a Implement Partial Hydration for Activity (#32863)
Stacked on #32862 and #32842.

This means that Activity boundaries now act as boundaries which can have
their effects mounted independently. Just like Suspense boundaries, we
hydrate the outer content first and then start hydrating the content in
an Offscreen lane. Flowing props or interacting with the content
increases the priority just like Suspense boundaries.

This skips emitting even the comments for `<Activity mode="hidden">` so
we don't hydrate those. Instead those are deferred to a later client
render.

The implementation are just forked copies of the SuspenseComponent
branches and then carefully going through each line and tweaking it.

The main interesting bit is that, unlike Suspense, Activity boundaries
don't have fallbacks so all those branches where you might commit a
suspended tree disappears. Instead, if something suspends while
hydration, we can just leave the dehydrated content in place. However,
if something does suspend during client rendering then it should bubble
up to the parent. Therefore, we have to be careful to only
pushSuspenseHandler when hydrating. That's really the main difference.

This just uses the existing basic Activity tests but I've started work
on port all of the applicable Suspense tests in SelectiveHydration-test
and PartialHydration-test to Activity versions.
2025-04-22 21:00:30 -04:00
Sebastian MarkbågeandGitHub 17f88c80ed Implement ActivityInstance in FiberConfigDOM (#32842)
Stacked on #32851 and #32900.

This implements the equivalent Configs for ActivityInstance as we have
for SuspenseInstance. These can be implemented as comments but they
don't have to be and can be implemented differently in the renderer.

This seems like a lot duplication but it's actually ends mostly just
calling the same methods underneath and the wrappers compiles out.

This doesn't leave the Activity dehydrated yet. It just hydrates into it
immediately.
2025-04-22 19:44:14 -04:00
Sebastian MarkbågeandGitHub 3fbd6b7b50 Set hidden Offscreen to the shellBoundary regardless of previous state (#32844)
I think this was probably just copy-paste from the Suspense path.

It shouldn't matter what the previous state of an Offscreen boundary
was. What matters is that it's now hidden and therefore if it suspends,
we can just leave it as is without the tree becoming inconsistent.
2025-04-22 19:39:09 -04:00
Sebastian MarkbågeandGitHub ebf7318e87 Hide/unhide the content of dehydrated suspense boundaries if they resuspend (#32900)
Found this bug while working on Activity. There's a weird edge case when
a dehydrated Suspense boundary is a direct child of another Suspense
boundary which is hydrated but then it resuspends without forcing the
inner one to hydrate/delete.

It used to just leave that in place because hiding/unhiding didn't deal
with dehydrated fragments.

Not sure this is really worth fixing.
2025-04-22 19:29:12 -04:00