## 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`
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.
e.g. `render(<Fragment ref={...}>{children}</Fragment>)` where
`children` contain no Host Components.
For `Document` we just noop because some part of the document is always
in view.
For `ShadowRoot` we use its `host`.
Any other `DocumentFragment` without a `host` issues a warning because
there's nothing to scroll. This matches the existing warning when you
call `scrollIntoView(false)` on an empty Fragment without siblings.
Notably this also applies to `<html><body /></html>` in `children`
because we skip `HostSingleton` at the moment.
---------
Co-authored-by: Hanzo Dev <[email protected]>
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.
When a Fragment has no children, React would consider scrolling to
siblings first and then to parents. However, React only considered
`HostComponent` for the siblings.
Since we already have a heuristic for scrolling to `HostText`, we can
reuse that same heuristic.
---------
Co-authored-by: Hanzo Dev <[email protected]>
We used to avoid updating .defaultValue until blur on number inputs
because of a Chrome bug, but the relevant Chromium behavior was fixed in
2020 in M85 https://issues.chromium.org/issues/40124871 and likewise in
WebKit https://bugs.webkit.org/show_bug.cgi?id=217156 so I think we can
probably just remove this special-cased logic.
We do still need some special logic for number inputs because it's the
only type where we let users specify a non-string value that has
multiple string representations and need to be careful not to clobber
the .value if it has a slightly different string than what you'd get
from casting a number to string.
Fixes#29862.
Tested with the number inputs fixture in current Chrome, Safari, and
Firefox.
## Summary
Mostly changing the casting syntax from `(x: Y)` to `x as Y`, as the old
syntax was deprecated and always causing an error in Flow
## How did you test this change?
`yarn flow-ci`
## Summary
Notable changes:
- Only $FlowFixMe, $FlowExpectedError suppression comments are supported
- All suppression comments need error code
- A lot of new invalid-compare and constant-condition errors suppressed.
These errors reveal potential logical bugs
## How did you test this change?
`yarn flow-ci`
Previously stylesheet resources would omit connecting with preloads
inserted via `preload` which caused unecessary suspension of commits
since the stylsheet resource would attempt to load the stylesheet again
and delay the initial commit or commit a fallback (depending on whether
the current screen should remain). This missing piece is that if you
preload a stylesheet you must be able to use that sheets loading state
when determining if the stylesheet is already loaded or not.
This adds a pending indicator on client inserted prelaod links. We still
assume SSR'd preloads are already loaded.
## Summary
`FragmentInstance.addEventListener` and `removeEventListener` fail to
cross-match listeners when the `capture` option is passed as a
**boolean** in one call and an **options object** in the other. This
violates the [DOM Living
Standard](https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener),
which states that `addEventListener(type, fn, true)` and
`addEventListener(type, fn, {capture: true})` are identical.
### Root Cause
In `ReactFiberConfigDOM.js`, the `normalizeListenerOptions` function
generates a listener key string for deduplication. The boolean branch
generates a **different format** than the object branch:
```js
// Boolean branch (old) — produces "c=1"
return `c=${opts ? '1' : '0'}`;
// Object branch — produces "c=1&o=0&p=0"
return `c=${opts.capture ? '1' : '0'}&o=${opts.once ? '1' : '0'}&p=${opts.passive ? '1' : '0'}`;
```
Because the keys differ, `indexOfEventListener` cannot match them — so
`removeEventListener('click', fn, {capture: true})` silently fails to
remove a listener registered with `addEventListener('click', fn, true)`,
and vice versa. This causes a **memory leak and event listener
accumulation** on all Fragment child DOM nodes.
### Fix
Normalize the boolean branch to produce the same full key format:
```js
// Boolean branch (fixed) — now produces "c=1&o=0&p=0" (matches object branch)
return `c=${opts ? '1' : '0'}&o=0&p=0`;
```
This makes both forms produce an identical key, matching the DOM spec
behavior.
### When Was This Introduced
This bug has been present since `FragmentInstance` event listener
tracking was first added. It became reachable in production as of
[#36026](https://github.com/facebook/react/pull/36026) which enabled
`enableFragmentRefs` + `enableFragmentRefsInstanceHandles` across all
builds (merged 3 days ago).
### Tests
Added two new regression tests to `ReactDOMFragmentRefs-test.js`:
1. `removes a capture listener registered with boolean when removed with
options object`
2. `removes a capture listener registered with options object when
removed with boolean`
Both tests were failing before this fix and pass after.
## How did you test this change?
Added two new automated tests covering both cross-form removal
directions. Existing tests continue to pass.
## Changelog
### React DOM
- **Fixed** `FragmentInstance.removeEventListener()` not removing
capture-phase listeners when the `capture` option form (boolean vs
options object) differs between `add` and `remove` calls.
## Summary
Follow-up to #36148 (which added credentialless as a recognized boolean
attribute for iframes). Adds credentialless to possibleStandardNames so
React's dev warning can suggest the correct casing when users write it
as Credentialless (or another incorrect case). Includes an SSR test
asserting the "Did you mean credentialless?" warning fires.
## Test plan
- yarn test ReactDOMComponent passes, including the new should warn
about incorrect casing on the credentialless property (ssr) case
## Summary
The `credentialless` attribute is a boolean HTML attribute for
`<iframe>` elements that loads the iframe in a new, ephemeral context
without access to the parent's credentials (cookies, client
certificates, etc.). This change adds it to all boolean attribute
switch/case lists in React DOM so it is properly handled as a boolean
(set when true, removed when false) rather than being treated as an
unknown string attribute.
Per the [Anonymous iframe spec
(WICG)](https://wicg.github.io/anonymous-iframe/):
> The credentialless attribute enables loading documents hosted by the
iframe with a new and ephemeral storage partition. It is a boolean
value. The default is false.
```
partial interface HTMLIFrameElement {
attribute boolean credentialless;
};
```
Changes:
- ReactDOMComponent.js: Added to both `setProp` and
`diffHydratedGenericElement`
- ReactFizzConfigDOM.js: Added to `pushAttribute` for server-side
rendering
- ReactDOMUnknownPropertyHook.js: Added to both validation switch/case
lists
## Test plan
- Added unit test in DOMPropertyOperations-test.js verifying
`credentialless={true}` sets the attribute to `''` and
`credentialless={false}` removes it
- All tests pass in source and www channels (590 tests each)
- Flow type checking passes (dom-node renderer)
- Prettier and lint pass
<!--
Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.
Before submitting a pull request, please make sure the following is
done:
1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
10. If you haven't already, complete the CLA.
Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->
## Summary
This PR fixes a few small spelling errors in comments across the
codebase (`teh`→`the`, `occuring`→`occurring`, `occured`→`occurred`). No
behavior changes.
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
## How did you test this change?
<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
If you leave this empty, your PR will very likely be closed.
-->
This is a comments-only change. I verified the diff is limited to
comment text and does not affect logic or runtime behavior.
<!--
Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.
Before submitting a pull request, please make sure the following is
done:
1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
10. If you haven't already, complete the CLA.
Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->
## Summary
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
I just fixed typos as followings.
- `succesful` → `successful`
- `becuase` → `because`
- `enought` → `enough`
- `defualt` → `default`
## How did you test this change?
<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
If you leave this empty, your PR will very likely be closed.
-->
This PR only includes test case description, dummy strings for test, and
comments updates, so it has no impact on runtime behavior.
Therefore, I manually reviewed changed texts to ensure correctness.
I found two focus bugs when working on documentation for Fragment Refs.
1) If an element delegates focus handling, it will return false from
setFocusIfFocusable even though a focus event has occured on a different
element. The fix for this is a document level event listener rather than
only listening on the current element.
For example, if you have a form with multiple nested label>inputs.
Calling focus on the label will focus its input but not fire an event on
the label. setFocusIfFocusable returns false and you end up continuing
to attempt focus down the form tree.
2) If an element is already focused, setFocusIfFocusable will return
false. The fix for this is checking the document's activeElement with an
early return.
In the same form example, if the first input is already focused and you
call fragmentInstance.focus() at the form level, the second input would
end up getting focused since the focus event on the first is not
triggered.
https://github.com/facebook/react/pull/34935 Introduced
`unstable_reactFragments` handle on DOM nodes to enable caching of
Observers.
This has been tested in production and is stable so it can be rolled out
with the Fragment Refs feature.
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.
Follow up to https://github.com/facebook/react/pull/35630
We don't currently have any operations that depend on the updating of
text nodes added or removed after Fragment mount. But for the sake of
completeness and extending the ability to any other host configs, this
change calls `commitNewChildToFragmentInstance` and
`deleteChildFromFragmentInstance` on HostText fibers.
Both DOM and Fabric configs early return because we cannot attach event
listeners or observers to text. In the future, there could be some
stateful Fragment feature that uses text that could extend this.
This PR adds text node support to FragmentInstance operations, allowing
fragment refs to properly handle fragments that contain text nodes
(either mixed with elements or text-only).
Not currently adding/removing new text nodes as we don't need to track
them for events or observers in DOM. Will follow up on this and with
Fabric support.
## Support through parent element
- `dispatchEvent`
- `compareDocumentPosition`
- `getRootNode`
## Support through Range API
- `getClientRects`: Uses Range to calculate bounding rects for text
nodes
- `scrollIntoView`: Uses Range to scroll to text node positions directly
## No support
- `focus`/`focusLast`/`blur`: Noop for text-only fragments
- `observeUsing`: Warns for text-only fragments in DEV
- `addEventListener`/`removeEventListener`: Ignores text nodes, but
still works on Fragment level through `dispatchEvent`
Follow up to #35559.
The clean up function of the custom timeline doesn't necessarily clean
up the animation. Just the timeline's internal state.
This affects Firefox which doesn't support ScrollTimeline so uses the
polyfill's custom timeline.
Currently we always clone the root when a gesture transition happens.
The was to add an optimization where if a Transition could be isolated
to an absolutely positioned subtree then we could just clone that
subtree or just do a plain insertion if it was simple an Enter. That way
when switching between two absolutely positioned pages the shell
wouldn't need to be cloned. In that case `detectMutationOrInsertClones`
would return false. However, currently it always return true because we
don't yet have that optimization.
The idea was to warn when the root required cloning to ensure that you
optimize it intentionally since it's easy to accidentally update more
than necessary. However, since this is not yet actionable I'm removing
this warning for now.
Instead, I add a warning for particularly bad cases where you really
shouldn't clone like iframe and video. They may not be very actionable
without the optimization since you can't scope it down to a subtree
without the optimization. So if they're above the gesture then they're
always cloned atm. However, it might also be that it's unnecessary to
keep them mounted if they could be removed or hidden with Activity.
Stacked on #35556 and #35559.
Given that we don't automatically clean up all view transition
animations since #35337 and browsers are buggy, it's important that you
clean up any `Animation` started manually from the events. However,
there was no clean up function for when the View Transition is forced to
stop. This also makes it harder to clean up custom timers etc too.
This lets you return a clean up function from all the events on
`<ViewTransition>`.
Follow up to #35337.
During a gesture, we always cancel the original animation and create a
new one that we control. That's the one we need to add to the set that
needs to be cancelled. Otherwise future gestures hang.
An unfortunate consequence is that any custom ones that you start e.g.
with #35556 or through other means aren't automatically cleaned up (in
fact there's not even a clean up callback yet). This can lead these to
freeze the whole UI afterwards. It would be really good to get this
fixed in browsers instead so we can revert #35337.
When the Fizz runtime runs a view-transition we apply
`view-transition-name` and `view-transition-class` to the `style`. These
can be observed by Fiber when hydrating which incorrectly leads to
hydration errors.
More over, even after we remove them, the `style` attribute has now been
normalized which we are unable to diff because we diff against the SSR
generated `style` attribute string and not the normalized form. So if
there are other inline styles defined, we have to skip diffing them in
this scenario.
Continue attaching `internalInstanceKey` to DOM nodes in DEV. This
prevents breaking some internal dev tooling while we experiment with the
broader change. Note that this does not reference the DOM handle within
the flag, just attaches it and deletes it. Internals tracking is still
done through the private map.
Speculative fix to https://github.com/facebook/react/issues/35336
written by Claude.
I have verified that applying a similar patch locally to the repro from
#35336 does fix the crash.
I'm not familiar enough with the underlying APIs to tell whether the fix
is correct or sufficient.
We already append `randomKey` to each handle name to prevent external
libraries from accessing and relying on these internals. But more
libraries recently have been getting around this by simply iterating
over the element properties and using a `startsWith` check.
This flag allows us to experiment with moving these handles to an
internal map.
This PR starts with the two most common internals, the props object and
the fiber. We can consider moving additional properties such as the
container root and others depending on perf results.
This is an alternative to #35059.
If the name needs escaping, then instead of escaping it, we just use a
base64 name. This wouldn't allow you to match on an escaped name in your
own CSS like you should be able to if browsers worked properly. But at
least it would provide matching name in current browsers which is
probably sufficient if you're using auto-generated names.
This also covers some cases where `CSS.escape()` isn't sufficient anyway
like when the name ends in a dot.
This PR adds a `unstable_reactFragments?: Set<FragmentInstance>`
property to DOM nodes that belong to a Fragment with a ref (top level
host components). This allows you to access a FragmentInstance from a
DOM node.
This is flagged behind `enableFragmentRefsInstanceHandles`.
The primary use case to unblock is reusing IntersectionObserver
instances. A fairly common practice is to cache and reuse
IntersectionObservers that share the same config, with a map of
node->callbacks to run for each entry in the IO callback. Currently this
is not possible with Fragment Ref `observeUsing` because the key in the
cache would have to be the `FragmentInstance` and you can't find it
without a handle from the node. This works now by accessing
`entry.target.fragments`.
This also opens up possibilities to use `FragmentInstance` operations in
other places, such as events. We can do
`event.target.unstable_reactFragments`, then access
`fragmentInstance.getClientRects` for example. In a future PR, we can
assign an event's `currentTarget` as the Fragment Ref for a more direct
handle when the event has been dispatched by the Fragment itself.
The first commit here implemented a handle only on observed elements.
This is awkward because there isn't a good way to document or expose
this temporary property. `element.fragments` is closer to what we would
expect from a DOM API if a standard was implemented here. And by
assigning it to all top-level nodes of a Fragment, it can be used beyond
the cached IntersectionObserver callback.
One tradeoff here is adding extra work during the creation of
FragmentInstances as well as keeping track of adding/removing nodes.
Previously we only track the Fiber on creation but here we add a
traversal which could apply to a large set of top-level host children.
The `element.unstable_reactFragments` Set can also be randomly ordered.
## Overview
This PR adds the `ref` prop to `<Fragment>` in `react@canary`.
This means this API is ready for final feedback and prepared for a
semver stable release.
## What this means
Shipping Fragment refs to canary means they have gone through extensive
testing in production, we are confident in the stability of the APIs,
and we are preparing to release it in a future semver stable version.
Libraries and frameworks following the [Canary
Workflow](https://react.dev/blog/2023/05/03/react-canaries) should begin
implementing and testing these features.
## Why we follow the Canary Workflow
To prepare for semver stable, libraries should test canary features like
Fragment refs with `react@canary` to confirm compatibility and prepare
for the next semver release in a myriad of environments and
configurations used throughout the React ecosystem. This provides
libraries with ample time to catch any issues we missed before slamming
them with problems in the wider semver release.
Since these features have already gone through extensive production
testing, and we are confident they are stable, frameworks following the
[Canary Workflow](https://react.dev/blog/2023/05/03/react-canaries) can
also begin adopting canary features like Fragment refs.
This adoption is similar to how different Browsers implement new
proposed browser features before they are added to the standard. If a
frameworks adopts a canary feature, they are committing to stability for
their users by ensuring any API changes before a semver stable release
are opaque and non-breaking to their users.
Apps not using a framework are also free to adopt canary features like
Fragment refs as long as they follow the [Canary
Workflow](https://react.dev/blog/2023/05/03/react-canaries), but we
generally recommend waiting for a semver stable release unless you have
the capacity to commit to following along with the canary changes and
debugging library compatibility issues.
Waiting for semver stable means you're able to benefit from libraries
testing and confirming support, and use semver as signal for which
version of a library you can use with support of the feature.
## Docs
Check out the ["React Labs: View Transitions, Activity, and
more"](https://react.dev/blog/2025/04/23/react-labs-view-transitions-activity-and-more#fragment-refs)
blog post, and [the new docs for Fragment
refs`](https://react.dev/reference/react/Fragment#fragmentinstance) for
more info.
The View Transition docs were unclear about this but apparently the
`finished` promise never settles if the animation never started. So if
there's an error that rejects the `ready` promise, we'll never run the
clean up which can cause it to stall.
Fixes#34662.
However, ultimately that is caused by Chrome stalling our default
`onDefaultTransitionIndicator` but it should be unblocked after 10
seconds, not a minute.