Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[Flight] Fix `encodeReply` for JSX with temporary references by unstubbable · Pull Request #35730 · react/react · GitHub
Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Flight] Fix `encodeReply` for JSX with temporary references by unstubbable · Pull Request #35730 · react/react · GitHub
Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Flight] Fix `encodeReply` for JSX with temporary references by unstubbable · Pull Request #35730 · react/react · GitHub
Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [Flight] Fix `encodeReply` for JSX with temporary references by unstubbable · Pull Request #35730 · react/react · GitHub
Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Flight] Fix `encodeReply` for JSX with temporary references by unstubbable · Pull Request #35730 · react/react · GitHub
Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Flight] Fix `encodeReply` for JSX with temporary references by unstubbable · Pull Request #35730 · react/react · GitHub
Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [Flight] Fix `encodeReply` for JSX with temporary references by unstubbable · Pull Request #35730 · react/react · GitHub
Skip to content

[Flight] Fix encodeReply for JSX with temporary references - #35730

Merged
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply
Feb 9, 2026
Merged

[Flight] Fix encodeReply for JSX with temporary references#35730
unstubbable merged 1 commit into
react:mainfrom
unstubbable:jsx-promise-encode-reply

Conversation

@unstubbable

Copy link
Copy Markdown
Collaborator

encodeReply throws "React Element cannot be passed to Server Functions from the Client without a temporary reference set" when a React element is the root value of a serializeModel call (either passed directly or resolved from a promise), even when a temporary reference set is provided.

The cause is that resolveToJSON hits the REACT_ELEMENT_TYPE switch case before reaching the existingReference/modelRoot check that regular objects benefit from. The synthetic JSON root created by JSON.stringify is never tracked in writtenObjects, so parentReference is undefined and the code falls through to the throw. This adds a modelRoot check in the REACT_ELEMENT_TYPE case, following the same pattern used for promises and plain objects.

The added JSX as root model test also uncovered a pre-existing crash in the Flight Client: when the JSX element round-trips back, it arrives as a frozen object (client-created elements are frozen in DEV), and Object.defineProperty for _debugInfo fails because frozen objects are non-configurable. The same crash can occur with JSX exported as a client reference. For now, we're adding !Object.isFrozen() guards in moveDebugInfoFromChunkToInnerValue and addAsyncInfo to prevent the crash, which means debug info is silently dropped for frozen elements. The proper fix would likely be to clone the element so each rendering context gets its own mutable copy with correct debug info.

closes#34984
closes#35690

`encodeReply` throws "React Element cannot be passed to Server Functions
from the Client without a temporary reference set" when a React element
is the root value of a `serializeModel` call (either passed directly or
resolved from a promise), even when a temporary reference set is
provided.
The cause is that `resolveToJSON` hits the `REACT_ELEMENT_TYPE` switch
case before reaching the `existingReference`/`modelRoot` check that
regular objects benefit from. The synthetic JSON root created by
`JSON.stringify` is never tracked in `writtenObjects`, so
`parentReference` is `undefined` and the code falls through to the
throw. This adds a `modelRoot` check in the `REACT_ELEMENT_TYPE` case,
following the same pattern used for promises and plain objects.
The added `JSX as root model` test also uncovered a pre-existing crash
in the Flight Client: when the JSX element round-trips back, it arrives
as a frozen object (client-created elements are frozen in DEV), and
`Object.defineProperty` for `_debugInfo` fails because frozen objects
are non-configurable. The same crash can occur with JSX exported as a
client reference. For now, we're adding `!Object.isFrozen()` guards in
`moveDebugInfoFromChunkToInnerValue` and `addAsyncInfo` to prevent the
crash, which means debug info is silently dropped for frozen elements.
The proper fix would likely be to clone the element so each rendering
context gets its own mutable copy with correct debug info.
closesreact#34984closesreact#35690
@github-actionsgithub-actionsBot added the React Core Team Opened by a member of the React Core Team label Feb 9, 2026
@react-sizebot

Copy link
Copy Markdown

Comparing: 2dd9b7c...a87af0f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-stable/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js=610.35 kB610.35 kB=107.89 kB107.89 kB
oss-experimental/react-dom/cjs/react-dom.production.js=6.84 kB6.84 kB=1.88 kB1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js=676.28 kB676.28 kB=118.85 kB118.85 kB
facebook-www/ReactDOM-prod.classic.js=696.77 kB696.77 kB=122.49 kB122.49 kB
facebook-www/ReactDOM-prod.modern.js=687.15 kB687.15 kB=120.89 kB120.89 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name+/-BaseCurrent+/- gzipBase gzipCurrent gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js+0.90%99.71 kB100.61 kB+1.30%20.28 kB20.54 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.05 kB234.09 kB+0.69%51.66 kB52.01 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.07 kB234.12 kB+0.69%51.68 kB52.04 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js+0.45%233.08 kB234.12 kB+0.69%51.68 kB52.04 kB

Generated by 🚫 dangerJS against a87af0f

@unstubbable
unstubbable marked this pull request as ready for review February 9, 2026 13:48
});

it('can pass JSX as root model through a round trip using temporary references', async () => {
const jsx = <div />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this server or client JSX?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client, which is the default in our tests, and we use ReactServer.createElement if we want server JSX.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss a proper fix.

@eps1loneps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with this since there's another bug where debug info is added multiple times for shared elements. No info is better than wrong info.

@unstubbable
unstubbable merged commit b07aa7d into react:mainFeb 9, 2026
243 checks passed
@unstubbable
unstubbable deleted the jsx-promise-encode-reply branch February 9, 2026 15:18
unstubbable pushed a commit to vercel/next.js that referenced this pull request Feb 9, 2026
m-kawafuji pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedReact Core TeamOpened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@unstubbable@react-sizebot@eps1lon@lubieowoce