Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fiber] support hydration when rendering Suspense anywhere #32224

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

gnoff
Copy link
Collaborator

@gnoff gnoff commented Jan 25, 2025

follow up to #32163

This continues the work of making Suspense workable anywhere in a react-dom tree. See the prior PRs for how we handle server rendering and client rendering. In this change we update the hydration implementation to be able to locate expected nodes. In particular this means hydration understands now that the default hydration context is the document body when the container is above the body.

One case that is unique to hydration is clearing Suspense boundaries. When hydration fails or when the server instructs the client to recover an errored boundary it's possible that the html, head, and body tags in the initial document were written from a fallback or a different primary content on the server and need to be replaced by the client render. However these tags (and in the case of head, their content) won't be inside the comment nodes that identify the bounds of the Suspense boundary. And when client rendering you may not even render the same singletons that were server rendered. So when server rendering a boudnary which contributes to the preamble (the html, head, and body tag openings plus the head contents) we emit a special marker comment just before closing the boundary out. This marker encodes which parts of the preamble this boundary owned. If we need to clear the suspense boundary on the client we read this marker and use it to reset the appropriate singleton state.

@react-sizebot
Copy link

react-sizebot commented Jan 25, 2025

Comparing: c492f97...d4928b8

Critical size changes

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

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.35% 515.10 kB 516.91 kB +0.42% 91.81 kB 92.20 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.32% 558.79 kB 560.60 kB +0.42% 99.13 kB 99.54 kB
facebook-www/ReactDOM-prod.classic.js +0.28% 597.07 kB 598.75 kB +0.36% 104.95 kB 105.32 kB
facebook-www/ReactDOM-prod.modern.js +0.29% 587.50 kB 589.18 kB +0.35% 103.41 kB 103.77 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.js +0.79% 228.10 kB 229.90 kB +0.54% 40.90 kB 41.12 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.js +0.79% 228.17 kB 229.98 kB +0.54% 40.93 kB 41.15 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.js +0.78% 229.85 kB 231.63 kB +0.52% 41.89 kB 42.10 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.js +0.78% 229.92 kB 231.71 kB +0.51% 41.91 kB 42.13 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.js +0.77% 233.30 kB 235.10 kB +0.54% 42.86 kB 43.09 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.js +0.77% 233.38 kB 235.18 kB +0.54% 42.89 kB 43.12 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.js +0.71% 254.27 kB 256.07 kB +0.46% 43.96 kB 44.16 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.js +0.71% 213.71 kB 215.23 kB +0.46% 39.45 kB 39.64 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.js +0.71% 213.79 kB 215.30 kB +0.46% 39.48 kB 39.66 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +0.70% 221.38 kB 222.93 kB +0.41% 40.93 kB 41.09 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.js +0.70% 256.09 kB 257.88 kB +0.44% 45.11 kB 45.31 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.js +0.69% 260.04 kB 261.84 kB +0.47% 46.03 kB 46.24 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.67% 209.41 kB 210.82 kB +0.43% 38.23 kB 38.40 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.67% 209.44 kB 210.84 kB +0.43% 38.26 kB 38.42 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.js +0.66% 213.93 kB 215.33 kB +0.44% 40.00 kB 40.17 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.js +0.66% 213.95 kB 215.36 kB +0.43% 40.03 kB 40.20 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.js +0.65% 232.87 kB 234.39 kB +0.42% 41.86 kB 42.04 kB
facebook-www/ReactDOMServer-prod.modern.js +0.65% 217.07 kB 218.47 kB +0.43% 39.30 kB 39.47 kB
facebook-www/ReactDOMServer-prod.classic.js +0.64% 219.75 kB 221.16 kB +0.42% 39.63 kB 39.80 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.js +0.62% 226.97 kB 228.38 kB +0.43% 40.50 kB 40.67 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.js +0.61% 232.05 kB 233.46 kB +0.42% 42.41 kB 42.58 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +0.56% 316.53 kB 318.31 kB +0.36% 61.61 kB 61.83 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +0.56% 316.60 kB 318.38 kB +0.36% 61.63 kB 61.86 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +0.56% 367.24 kB 369.29 kB +0.35% 66.19 kB 66.42 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +0.56% 367.31 kB 369.36 kB +0.35% 66.24 kB 66.47 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +0.56% 370.69 kB 372.76 kB +0.33% 66.83 kB 67.05 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +0.56% 370.77 kB 372.84 kB +0.33% 66.88 kB 67.10 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.development.js +0.56% 371.46 kB 373.53 kB +0.33% 66.99 kB 67.21 kB
oss-stable/react-dom/cjs/react-dom-server.edge.development.js +0.56% 371.54 kB 373.61 kB +0.33% 67.05 kB 67.27 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +0.55% 363.39 kB 365.37 kB +0.35% 65.68 kB 65.91 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +0.52% 343.60 kB 345.37 kB +0.34% 65.21 kB 65.44 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +0.52% 355.25 kB 357.08 kB +0.37% 64.66 kB 64.89 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.52% 355.25 kB 357.09 kB +0.37% 64.66 kB 64.89 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +0.52% 355.27 kB 357.11 kB +0.37% 64.68 kB 64.92 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.52% 355.28 kB 357.11 kB +0.36% 64.68 kB 64.92 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +0.51% 405.52 kB 407.57 kB +0.32% 70.56 kB 70.78 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +0.50% 409.57 kB 411.63 kB +0.31% 71.11 kB 71.33 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +0.50% 410.35 kB 412.42 kB +0.35% 71.27 kB 71.51 kB
facebook-www/ReactDOMServer-dev.modern.js +0.49% 371.83 kB 373.67 kB +0.36% 66.94 kB 67.18 kB
facebook-www/ReactDOMServer-dev.classic.js +0.48% 378.66 kB 380.49 kB +0.37% 67.95 kB 68.20 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +0.48% 384.72 kB 386.56 kB +0.37% 68.24 kB 68.49 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.48% 384.72 kB 386.56 kB +0.36% 68.24 kB 68.49 kB
oss-experimental/react-markup/cjs/react-markup.production.js +0.42% 215.24 kB 216.14 kB +0.19% 39.74 kB 39.82 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js +0.35% 514.97 kB 516.78 kB +0.42% 91.78 kB 92.17 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.35% 515.10 kB 516.91 kB +0.42% 91.81 kB 92.20 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js +0.33% 545.38 kB 547.19 kB +0.42% 96.51 kB 96.92 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js +0.33% 545.50 kB 547.31 kB +0.42% 96.54 kB 96.94 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js +0.33% 540.65 kB 542.43 kB +0.43% 95.87 kB 96.29 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js +0.33% 546.16 kB 547.94 kB +0.43% 96.95 kB 97.37 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.32% 558.79 kB 560.60 kB +0.42% 99.13 kB 99.54 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js +0.32% 573.52 kB 575.33 kB +0.37% 102.73 kB 103.11 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js +0.32% 565.49 kB 567.27 kB +0.42% 99.61 kB 100.03 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js +0.31% 571.43 kB 573.21 kB +0.42% 100.76 kB 101.18 kB
oss-experimental/react-markup/cjs/react-markup.development.js +0.31% 356.28 kB 357.37 kB +0.16% 63.97 kB 64.07 kB
oss-experimental/react-dom/cjs/react-dom-profiling.profiling.js +0.29% 614.48 kB 616.29 kB +0.37% 107.85 kB 108.25 kB
oss-experimental/react-markup/cjs/react-markup.react-server.production.js +0.29% 314.98 kB 315.88 kB +0.13% 58.89 kB 58.97 kB
facebook-www/ReactDOM-prod.modern.js +0.29% 587.50 kB 589.18 kB +0.35% 103.41 kB 103.77 kB
facebook-www/ReactDOM-prod.classic.js +0.28% 597.07 kB 598.75 kB +0.36% 104.95 kB 105.32 kB
facebook-www/ReactDOMTesting-prod.modern.js +0.28% 602.22 kB 603.90 kB +0.35% 107.10 kB 107.48 kB
facebook-www/ReactDOMTesting-prod.classic.js +0.27% 611.79 kB 613.47 kB +0.36% 108.64 kB 109.03 kB
facebook-www/ReactDOM-profiling.modern.js +0.27% 614.51 kB 616.19 kB +0.36% 107.25 kB 107.63 kB
facebook-www/ReactDOM-profiling.classic.js +0.27% 624.13 kB 625.81 kB +0.36% 108.83 kB 109.22 kB
oss-stable-semver/react-dom/cjs/react-dom-client.development.js +0.24% 943.54 kB 945.78 kB +0.30% 159.26 kB 159.73 kB
oss-stable/react-dom/cjs/react-dom-client.development.js +0.24% 943.66 kB 945.90 kB +0.30% 159.29 kB 159.76 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.development.js +0.23% 959.98 kB 962.22 kB +0.29% 162.09 kB 162.57 kB
oss-stable/react-dom/cjs/react-dom-profiling.development.js +0.23% 960.10 kB 962.34 kB +0.29% 162.12 kB 162.60 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-dev.js +0.23% 993.63 kB 995.87 kB +0.29% 166.99 kB 167.48 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-dev.js +0.22% 1,009.97 kB 1,012.21 kB +0.30% 169.81 kB 170.33 kB
oss-experimental/react-dom/cjs/react-dom-client.development.js +0.22% 1,029.73 kB 1,031.97 kB +0.27% 172.26 kB 172.72 kB
oss-experimental/react-dom/cjs/react-dom-profiling.development.js +0.21% 1,046.13 kB 1,048.37 kB +0.27% 175.11 kB 175.58 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.development.js +0.21% 1,046.65 kB 1,048.89 kB +0.27% 176.00 kB 176.48 kB
facebook-www/ReactDOM-dev.modern.js +0.21% 1,063.05 kB 1,065.27 kB +0.28% 177.05 kB 177.54 kB
facebook-www/ReactDOM-dev.classic.js +0.21% 1,072.19 kB 1,074.41 kB +0.29% 178.76 kB 179.28 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js +0.21% 529.48 kB 530.56 kB +0.11% 94.93 kB 95.04 kB
facebook-www/ReactDOMTesting-dev.modern.js +0.21% 1,079.95 kB 1,082.17 kB +0.28% 180.95 kB 181.46 kB
facebook-www/ReactDOMTesting-dev.classic.js +0.20% 1,089.09 kB 1,091.32 kB +0.28% 182.67 kB 183.18 kB

Generated by 🚫 dangerJS against a85088e

@@ -948,6 +993,37 @@ export function clearSuspenseBoundary(
data === SUSPENSE_FALLBACK_START_DATA
) {
depth++;
} else if (data.length === 1) {
Copy link
Collaborator Author

@gnoff gnoff Jan 27, 2025

Choose a reason for hiding this comment

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

This may be a bit bold but rendering comments in React components it's super common and this is only run on dehydrated suspense boundaries so we're generally only susceptible to colliding with user of innerHTML. We can't eliminate every collision so while prefixing with $ and this style of comment is not going to be written by a user so the most likely consequence is that there is a collision with an extension.

If we want to be significantly more defensive we can add a check that the nextSibling is also a comment matching the Suspense end marker. But I don't think it's actually worth the code size to include

stacked on facebook#32163

This continues the work of making Suspense workable anywhere in a react-dom tree. See the prior PRs for how we handle server rendering and client rendering. In this change we update the hydration implementation to be able to locate expected nodes. In particular this means hydration understands now that the default hydration context is the document body when the container is above the body.

One case that is unique to hydration is clearing Suspense boundaries. When hydration fails or when the server instructs the client to recover an errored boundary it's possible that the html, head, and body tags in the initial document were written from a fallback or a different primary content on the server and need to be replaced by the client render. However these tags (and in the case of head, their content) won't be inside the comment nodes that identify the bounds of the Suspense boundary. And when client rendering you may not even render the same singletons that were server rendered. So when server rendering a boudnary which contributes to the preamble (the html, head, and body tag openings plus the head contents) we emit a special marker comment just before closing the boundary out. This marker encodes which parts of the preamble this boundary owned. If we need to clear the suspense boundary on the client we read this marker and use it to reset the appropriate singleton state.
@gnoff gnoff force-pushed the suspense-anywhere-hydration branch 2 times, most recently from b7ed994 to a85088e Compare January 29, 2025 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants