Skip to content

[Fizz] implement renderIntoContainer #25703

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

Closed
wants to merge 9 commits into from

Conversation

gnoff
Copy link
Collaborator

@gnoff gnoff commented Nov 18, 2022

Implements renderIntoContainer and renderIntoContainerAsPipeableStream

Streaming rendering similar to renderToReadableStream and renderToPipeableStream but will hoist root most content into a container element in the DOM. This is useful for people who want to take advantage of React's streaming rendering but have an existing system which produces the "Shell" of the app.

// in your html
<html>
  <body>
    <div id="someId">
    </div>
  </body>
</html>
// React content will stream in here
// in your server
import { renderIntoContainer } from 'react-dom/server'

const stream = renderIntoContainer(<div>hello world</div>, "someId");
// stream this output after sending the html shell above

This will produce

// in your html
<html>
  <body>
    <div id="someId">
      <div>hello world</div>
    </div>
  </body>
</html>

These new methods don't have a Shell. Instead the topmost children are treated like they are wrapped in a Suspense boundary. the content will be inserted into the container on the client when there are no more pending tasks for this Root Boundary. Stylesheets get emitted as part of the boundary completion just like normal Suspsense boundaries. Other resources will get streamed in as early as possible but not relocated so if you use preloads or prinits of scripts they will likely show up in the body and stay there.

Certain options don't make sense for these methods. There is no onShellReady or onShellError because there is no shell.
In the ReadableStream case you get your stream synchronously rather than having to wait for a promsie to resolve when the shell is ready.

@sizebot
Copy link

sizebot commented Nov 23, 2022

Comparing: b14d7fa...6fa5d40

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.min.js = 154.37 kB 154.37 kB = 48.97 kB 48.97 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 156.29 kB 156.29 kB = 49.63 kB 49.63 kB
facebook-www/ReactDOM-prod.classic.js = 533.12 kB 533.12 kB = 94.96 kB 94.96 kB
facebook-www/ReactDOM-prod.modern.js = 518.22 kB 518.22 kB = 92.76 kB 92.76 kB
facebook-www/ReactDOMForked-prod.classic.js = 533.12 kB 533.12 kB = 94.96 kB 94.96 kB
oss-experimental/react-dom/server.node.js +13.47% 0.65 kB 0.73 kB +8.40% 0.25 kB 0.27 kB
oss-stable-semver/react-dom/server.node.js +13.47% 0.65 kB 0.73 kB +8.40% 0.25 kB 0.27 kB
oss-stable/react-dom/server.node.js +13.47% 0.65 kB 0.73 kB +8.40% 0.25 kB 0.27 kB
oss-experimental/react-dom/unstable_server-external-runtime.js +12.93% 2.51 kB 2.84 kB +9.48% 1.14 kB 1.25 kB
oss-stable-semver/react-dom/unstable_server-external-runtime.js +12.93% 2.51 kB 2.84 kB +9.48% 1.14 kB 1.25 kB
oss-stable/react-dom/unstable_server-external-runtime.js +12.93% 2.51 kB 2.84 kB +9.48% 1.14 kB 1.25 kB
oss-experimental/react-dom/server.browser.js +8.05% 0.66 kB 0.71 kB +5.93% 0.25 kB 0.27 kB
oss-stable-semver/react-dom/server.browser.js +8.05% 0.66 kB 0.71 kB +5.93% 0.25 kB 0.27 kB
oss-stable/react-dom/server.browser.js +8.05% 0.66 kB 0.71 kB +5.93% 0.25 kB 0.27 kB
oss-experimental/react-dom/server.bun.js +7.49% 0.65 kB 0.70 kB +5.08% 0.26 kB 0.27 kB
oss-stable-semver/react-dom/server.bun.js +7.49% 0.65 kB 0.70 kB +5.08% 0.26 kB 0.27 kB
oss-stable/react-dom/server.bun.js +7.49% 0.65 kB 0.70 kB +5.08% 0.26 kB 0.27 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.min.js +5.69% 54.79 kB 57.91 kB +3.77% 17.08 kB 17.73 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.min.js +5.69% 54.81 kB 57.93 kB +3.75% 17.11 kB 17.75 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.min.js +5.65% 55.20 kB 58.32 kB +3.73% 17.27 kB 17.91 kB
facebook-www/ReactDOMServer-prod.modern.js +4.84% 123.73 kB 129.72 kB +3.66% 23.42 kB 24.27 kB
facebook-www/ReactDOMServer-prod.classic.js +4.73% 126.87 kB 132.87 kB +3.82% 24.04 kB 24.96 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +4.72% 128.86 kB 134.94 kB +3.64% 24.73 kB 25.63 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +3.60% 50.77 kB 52.60 kB +2.33% 15.60 kB 15.96 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +3.59% 50.79 kB 52.62 kB +2.32% 15.62 kB 15.99 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +3.57% 51.12 kB 52.95 kB +2.26% 15.77 kB 16.12 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.production.min.js +3.56% 50.87 kB 52.69 kB +2.31% 15.92 kB 16.29 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.production.min.js +3.56% 50.90 kB 52.71 kB +2.32% 15.94 kB 16.31 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js +3.54% 51.23 kB 53.04 kB +2.30% 16.06 kB 16.43 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.min.js +3.29% 55.49 kB 57.32 kB +2.19% 17.05 kB 17.42 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.min.js +3.29% 55.51 kB 57.34 kB +2.19% 17.07 kB 17.45 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js +3.27% 55.90 kB 57.73 kB +2.12% 17.23 kB 17.60 kB
facebook-www/ReactDOMServer-dev.modern.js +3.08% 337.37 kB 347.77 kB +1.66% 75.12 kB 76.37 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +3.07% 332.61 kB 342.84 kB +1.65% 74.04 kB 75.27 kB
facebook-www/ReactDOMServer-dev.classic.js +3.02% 344.06 kB 354.46 kB +1.64% 76.54 kB 77.80 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js +2.98% 52.01 kB 53.56 kB +3.68% 16.55 kB 17.16 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js +2.97% 52.03 kB 53.58 kB +3.68% 16.57 kB 17.18 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +2.96% 52.36 kB 53.91 kB +3.57% 16.70 kB 17.30 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +2.95% 320.20 kB 329.66 kB +1.70% 73.73 kB 74.98 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +2.95% 320.23 kB 329.68 kB +1.70% 73.75 kB 75.00 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js +2.95% 52.08 kB 53.61 kB +3.64% 16.85 kB 17.46 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js +2.94% 52.10 kB 53.64 kB +3.64% 16.87 kB 17.48 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +2.94% 321.52 kB 330.98 kB +1.70% 74.10 kB 75.37 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +2.93% 52.43 kB 53.97 kB +3.59% 16.98 kB 17.59 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +2.91% 317.02 kB 326.25 kB +1.62% 72.85 kB 74.03 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +2.91% 317.04 kB 326.28 kB +1.62% 72.87 kB 74.05 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.development.js +2.91% 335.76 kB 345.54 kB +1.63% 74.50 kB 75.72 kB
oss-stable/react-dom/umd/react-dom-server.browser.development.js +2.91% 335.78 kB 345.57 kB +1.63% 74.52 kB 75.74 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +2.90% 318.33 kB 327.57 kB +1.61% 73.23 kB 74.41 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +2.90% 337.16 kB 346.94 kB +1.63% 74.87 kB 76.09 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js +2.90% 56.31 kB 57.95 kB +3.29% 17.87 kB 18.46 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js +2.90% 56.34 kB 57.97 kB +3.29% 17.89 kB 18.48 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +2.90% 321.43 kB 330.74 kB +1.58% 73.68 kB 74.84 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +2.90% 321.45 kB 330.76 kB +1.58% 73.70 kB 74.86 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +2.88% 322.75 kB 332.06 kB +1.59% 74.05 kB 75.22 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +2.88% 56.73 kB 58.36 kB +3.24% 18.03 kB 18.62 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.development.js +2.39% 320.83 kB 328.50 kB +1.48% 73.92 kB 75.01 kB
oss-experimental/react-dom/cjs/react-dom-static.node.development.js +2.38% 322.71 kB 330.38 kB +1.48% 74.14 kB 75.24 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +2.31% 319.69 kB 327.06 kB +1.46% 73.26 kB 74.33 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +2.31% 319.72 kB 327.09 kB +1.46% 73.28 kB 74.35 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +2.30% 321.01 kB 328.38 kB +1.46% 73.64 kB 74.71 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +2.29% 321.40 kB 328.77 kB +1.45% 73.73 kB 74.80 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +2.29% 321.43 kB 328.80 kB +1.45% 73.75 kB 74.82 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +2.28% 322.72 kB 330.09 kB +1.45% 74.11 kB 75.18 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.development.js +2.27% 335.23 kB 342.84 kB +1.44% 74.05 kB 75.11 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.development.js +2.27% 335.25 kB 342.86 kB +1.44% 74.07 kB 75.13 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +2.26% 336.63 kB 344.24 kB +1.43% 74.42 kB 75.48 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js = 23.26 kB 22.49 kB +1.17% 8.19 kB 8.28 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js = 23.22 kB 22.45 kB +1.19% 8.17 kB 8.27 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js = 23.22 kB 22.45 kB +1.19% 8.17 kB 8.27 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js = 23.07 kB 22.30 kB +1.26% 8.20 kB 8.31 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js = 23.03 kB 22.26 kB +1.27% 8.19 kB 8.29 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js = 23.03 kB 22.26 kB +1.27% 8.19 kB 8.29 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js = 22.89 kB 22.12 kB +1.26% 8.11 kB 8.21 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js = 22.84 kB 22.07 kB +1.29% 8.09 kB 8.19 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js = 22.84 kB 22.07 kB +1.29% 8.09 kB 8.19 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-dom/server.node.js +13.47% 0.65 kB 0.73 kB +8.40% 0.25 kB 0.27 kB
oss-stable-semver/react-dom/server.node.js +13.47% 0.65 kB 0.73 kB +8.40% 0.25 kB 0.27 kB
oss-stable/react-dom/server.node.js +13.47% 0.65 kB 0.73 kB +8.40% 0.25 kB 0.27 kB
oss-experimental/react-dom/unstable_server-external-runtime.js +12.93% 2.51 kB 2.84 kB +9.48% 1.14 kB 1.25 kB
oss-stable-semver/react-dom/unstable_server-external-runtime.js +12.93% 2.51 kB 2.84 kB +9.48% 1.14 kB 1.25 kB
oss-stable/react-dom/unstable_server-external-runtime.js +12.93% 2.51 kB 2.84 kB +9.48% 1.14 kB 1.25 kB
oss-experimental/react-dom/server.browser.js +8.05% 0.66 kB 0.71 kB +5.93% 0.25 kB 0.27 kB
oss-stable-semver/react-dom/server.browser.js +8.05% 0.66 kB 0.71 kB +5.93% 0.25 kB 0.27 kB
oss-stable/react-dom/server.browser.js +8.05% 0.66 kB 0.71 kB +5.93% 0.25 kB 0.27 kB
oss-experimental/react-dom/server.bun.js +7.49% 0.65 kB 0.70 kB +5.08% 0.26 kB 0.27 kB
oss-stable-semver/react-dom/server.bun.js +7.49% 0.65 kB 0.70 kB +5.08% 0.26 kB 0.27 kB
oss-stable/react-dom/server.bun.js +7.49% 0.65 kB 0.70 kB +5.08% 0.26 kB 0.27 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.min.js +5.69% 54.79 kB 57.91 kB +3.77% 17.08 kB 17.73 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.min.js +5.69% 54.81 kB 57.93 kB +3.75% 17.11 kB 17.75 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.min.js +5.65% 55.20 kB 58.32 kB +3.73% 17.27 kB 17.91 kB
facebook-www/ReactDOMServer-prod.modern.js +4.84% 123.73 kB 129.72 kB +3.66% 23.42 kB 24.27 kB
facebook-www/ReactDOMServer-prod.classic.js +4.73% 126.87 kB 132.87 kB +3.82% 24.04 kB 24.96 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +4.72% 128.86 kB 134.94 kB +3.64% 24.73 kB 25.63 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +3.60% 50.77 kB 52.60 kB +2.33% 15.60 kB 15.96 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +3.59% 50.79 kB 52.62 kB +2.32% 15.62 kB 15.99 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +3.57% 51.12 kB 52.95 kB +2.26% 15.77 kB 16.12 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.production.min.js +3.56% 50.87 kB 52.69 kB +2.31% 15.92 kB 16.29 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.production.min.js +3.56% 50.90 kB 52.71 kB +2.32% 15.94 kB 16.31 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js +3.54% 51.23 kB 53.04 kB +2.30% 16.06 kB 16.43 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.min.js +3.29% 55.49 kB 57.32 kB +2.19% 17.05 kB 17.42 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.min.js +3.29% 55.51 kB 57.34 kB +2.19% 17.07 kB 17.45 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js +3.27% 55.90 kB 57.73 kB +2.12% 17.23 kB 17.60 kB
facebook-www/ReactDOMServer-dev.modern.js +3.08% 337.37 kB 347.77 kB +1.66% 75.12 kB 76.37 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +3.07% 332.61 kB 342.84 kB +1.65% 74.04 kB 75.27 kB
facebook-www/ReactDOMServer-dev.classic.js +3.02% 344.06 kB 354.46 kB +1.64% 76.54 kB 77.80 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js +2.98% 52.01 kB 53.56 kB +3.68% 16.55 kB 17.16 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js +2.97% 52.03 kB 53.58 kB +3.68% 16.57 kB 17.18 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +2.96% 52.36 kB 53.91 kB +3.57% 16.70 kB 17.30 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +2.95% 320.20 kB 329.66 kB +1.70% 73.73 kB 74.98 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +2.95% 320.23 kB 329.68 kB +1.70% 73.75 kB 75.00 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js +2.95% 52.08 kB 53.61 kB +3.64% 16.85 kB 17.46 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js +2.94% 52.10 kB 53.64 kB +3.64% 16.87 kB 17.48 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +2.94% 321.52 kB 330.98 kB +1.70% 74.10 kB 75.37 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +2.93% 52.43 kB 53.97 kB +3.59% 16.98 kB 17.59 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +2.91% 317.02 kB 326.25 kB +1.62% 72.85 kB 74.03 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +2.91% 317.04 kB 326.28 kB +1.62% 72.87 kB 74.05 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.development.js +2.91% 335.76 kB 345.54 kB +1.63% 74.50 kB 75.72 kB
oss-stable/react-dom/umd/react-dom-server.browser.development.js +2.91% 335.78 kB 345.57 kB +1.63% 74.52 kB 75.74 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +2.90% 318.33 kB 327.57 kB +1.61% 73.23 kB 74.41 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +2.90% 337.16 kB 346.94 kB +1.63% 74.87 kB 76.09 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js +2.90% 56.31 kB 57.95 kB +3.29% 17.87 kB 18.46 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js +2.90% 56.34 kB 57.97 kB +3.29% 17.89 kB 18.48 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +2.90% 321.43 kB 330.74 kB +1.58% 73.68 kB 74.84 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +2.90% 321.45 kB 330.76 kB +1.58% 73.70 kB 74.86 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +2.88% 322.75 kB 332.06 kB +1.59% 74.05 kB 75.22 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +2.88% 56.73 kB 58.36 kB +3.24% 18.03 kB 18.62 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.development.js +2.39% 320.83 kB 328.50 kB +1.48% 73.92 kB 75.01 kB
oss-experimental/react-dom/cjs/react-dom-static.node.development.js +2.38% 322.71 kB 330.38 kB +1.48% 74.14 kB 75.24 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +2.31% 319.69 kB 327.06 kB +1.46% 73.26 kB 74.33 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +2.31% 319.72 kB 327.09 kB +1.46% 73.28 kB 74.35 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +2.30% 321.01 kB 328.38 kB +1.46% 73.64 kB 74.71 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +2.29% 321.40 kB 328.77 kB +1.45% 73.73 kB 74.80 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +2.29% 321.43 kB 328.80 kB +1.45% 73.75 kB 74.82 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +2.28% 322.72 kB 330.09 kB +1.45% 74.11 kB 75.18 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.development.js +2.27% 335.23 kB 342.84 kB +1.44% 74.05 kB 75.11 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.development.js +2.27% 335.25 kB 342.86 kB +1.44% 74.07 kB 75.13 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +2.26% 336.63 kB 344.24 kB +1.43% 74.42 kB 75.48 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.production.min.js +1.40% 52.24 kB 52.97 kB +2.63% 16.65 kB 17.09 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-server.production.min.js +1.30% 3.24 kB 3.28 kB +0.74% 1.21 kB 1.22 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-server.production.min.js +1.30% 3.24 kB 3.28 kB +0.74% 1.21 kB 1.22 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-server.production.min.js +1.30% 3.24 kB 3.28 kB +0.74% 1.21 kB 1.22 kB
oss-experimental/react-dom/cjs/react-dom-static.node.production.min.js +1.26% 56.73 kB 57.44 kB +2.66% 18.02 kB 18.50 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +1.18% 84.50 kB 85.50 kB +1.00% 21.14 kB 21.35 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +1.18% 84.50 kB 85.50 kB +1.00% 21.14 kB 21.35 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +1.18% 84.56 kB 85.55 kB +0.99% 21.16 kB 21.37 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +1.16% 85.75 kB 86.75 kB +0.98% 21.21 kB 21.42 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +1.16% 85.75 kB 86.75 kB +0.98% 21.21 kB 21.42 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +1.16% 85.81 kB 86.80 kB +0.96% 21.23 kB 21.43 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js +1.15% 88.71 kB 89.72 kB +0.96% 21.39 kB 21.60 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js +1.15% 88.71 kB 89.72 kB +0.96% 21.39 kB 21.60 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js +1.15% 88.77 kB 89.79 kB +0.94% 21.41 kB 21.61 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.87% 6.53 kB 6.58 kB +0.28% 1.80 kB 1.81 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.87% 6.53 kB 6.58 kB +0.28% 1.80 kB 1.81 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.87% 6.53 kB 6.58 kB +0.28% 1.80 kB 1.81 kB
oss-stable-semver/react-server/cjs/react-server.production.min.js +0.69% 24.27 kB 24.43 kB +0.70% 8.30 kB 8.36 kB
oss-stable/react-server/cjs/react-server.production.min.js +0.69% 24.27 kB 24.43 kB +0.70% 8.30 kB 8.36 kB
oss-experimental/react-server/cjs/react-server.production.min.js +0.69% 24.51 kB 24.68 kB +0.67% 8.39 kB 8.44 kB
oss-stable-semver/react-server/cjs/react-server.development.js +0.32% 145.62 kB 146.08 kB +0.21% 36.24 kB 36.31 kB
oss-stable/react-server/cjs/react-server.development.js +0.32% 145.62 kB 146.08 kB +0.21% 36.24 kB 36.31 kB
oss-experimental/react-server/cjs/react-server.development.js +0.32% 146.24 kB 146.70 kB +0.24% 36.42 kB 36.51 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js = 23.26 kB 22.49 kB +1.17% 8.19 kB 8.28 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js = 23.22 kB 22.45 kB +1.19% 8.17 kB 8.27 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js = 23.22 kB 22.45 kB +1.19% 8.17 kB 8.27 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js = 23.07 kB 22.30 kB +1.26% 8.20 kB 8.31 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js = 23.03 kB 22.26 kB +1.27% 8.19 kB 8.29 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js = 23.03 kB 22.26 kB +1.27% 8.19 kB 8.29 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js = 22.89 kB 22.12 kB +1.26% 8.11 kB 8.21 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js = 22.84 kB 22.07 kB +1.29% 8.09 kB 8.19 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js = 22.84 kB 22.07 kB +1.29% 8.09 kB 8.19 kB

Generated by 🚫 dangerJS against 6fa5d40

@gnoff gnoff force-pushed the fizz-into-container branch from 4d43398 to 8ed0dfb Compare November 23, 2022 18:05
@tannerlinsley
Copy link

tannerlinsley commented Dec 2, 2022

Is this going to enable the ability to stream a React application that is responsible for rendering <html> and friends? eg.

function App() {
  return (
    <Suspense>
      <html>
        <head></head>
        <body></body>
      </html>
    </Suspense>
  )
}

@gaearon
Copy link
Collaborator

gaearon commented Dec 8, 2022

@tannerlinsley I believe it's the other way around — this adds support for not rendering the entire document with React.

@gnoff
Copy link
Collaborator Author

gnoff commented Dec 8, 2022

That's right. Right now renderToReadableStream really only makes sense if you render from <html> or maybe from <body>.

The point of this new API is to stream into some DOM element that was sent via other means like a static html file. If you send the HTML first and then send the stream response this api will relocate the streamed in content into a dom node that has the ID you pass in.

@gnoff gnoff force-pushed the fizz-into-container branch 4 times, most recently from 766245c to a673ba5 Compare December 10, 2022 02:53
Comment on lines 145 to 150
if (isContainer) {
const containerNode = document.getElementById(suspenseBoundaryID);
if (containerNode) {
containerNode['_r'] = allInsertions;
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is how we "block" hydration until the boundary can switch the content in. There are still an unhandled edge cases though

For fizz runtime the script loads async so it's possible the bootstrap scripts execute first. This means a hydrateRoot call might start to compare HTML before the actual content is swapped in. I don't really have a good idea atm how to defend against this. It may require coordination with the container itself (an attribute that is removed when hydration is unblocked)

cc @sebmarkbage

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

cc @mofeiZ as well

@gnoff gnoff marked this pull request as ready for review December 10, 2022 21:01
@gnoff gnoff requested review from acdlite and sebmarkbage and removed request for acdlite December 10, 2022 21:01
@gnoff gnoff force-pushed the fizz-into-container branch from 6fa5d40 to 80ad0df Compare January 4, 2023 00:23
@@ -133,6 +139,86 @@ async function replaceScriptsAndMove(
}
}

export function installScriptObserver(window: any, document: Document) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@mofeiZ I was having trouble getting the latest technique of delaying bootstrap scripts to work with the current test setup for moving scripts. The problem is that in an async tick a script is added to the body but there is no visibility into this event for us to use the trick we use in act.

I implemented an entirely different approach where we use a mutation observer and look for unexecuted scripts on each new node addition. I believe this approach can work for all prior and new use cases but I may not understand well why mutation observer wasn't used in the first place so I would love a review here especially to see if I may need to make changes.

I took this opportunity to also make act better. It now infers the insertion point based on what is being streamed in so we don't need actIntoEntireDocument. I renamed the original act to actIntoContainer. it's not representative of 99% of how streaming will be done so I made it not the default name and we should probably move away from using it

Comment on lines +168 to +180
const scriptObserverFunctionString = `
const scriptObserver = new MutationObserver(mutations => {
let promises = [];
for (var i = 0; i < mutations.length; i++) {
const addedNodes = mutations[i].addedNodes;
if (addedNodes.length) {
var pendingWork = window.__loadScripts(window);
window.__pendingWork = (window.__pendingWork || Promise.resolve()).then(() => pendingWork);
// We can stop here because loadScripts loads scripts globally so there
// is not additional benefit to calling it for each mutation that added nodes
}
}
});

window.__scriptObserver = scriptObserver;
`;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason this is a string and is eval'd is because the MutationObserver does not think the Node's passed in are actually Nodes. This is because window.Node is not in the prototype chain in the context of this file since we are using an adhoc jsdom window not the actual global. @mofeiZ if you can think of way to do this so we don't need to have this code as a string I'm open to suggestions :)

Copy link
Contributor

Choose a reason for hiding this comment

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

@gnoff I think JSDOM window provides a mocked MutationObserver. Fizz tests still pass when I remove eval(...) and call new window.MutationObserver(...)

function makeScriptObserver(window: JSDOM.DOMWindow) {
  return window.__scriptObserver = new window.MutationObserver(...);
}

@gnoff gnoff force-pushed the fizz-into-container branch from 58941bc to 7c39cd5 Compare January 6, 2023 06:05
@gnoff
Copy link
Collaborator Author

gnoff commented Jan 6, 2023

@sebmarkbage worked out the issues with external fizz runtime. this PR is ready for review now

@gnoff gnoff force-pushed the fizz-into-container branch from 5ac3ef8 to 688be00 Compare January 6, 2023 16:22
@gnoff gnoff force-pushed the fizz-into-container branch 2 times, most recently from 986dbbd to 95c9a73 Compare January 10, 2023 22:45
@gnoff gnoff changed the title [Fizz] implement basic streaming for renderIntoContainer [Fizz] implement renderIntoContainer Jan 11, 2023
@gnoff gnoff force-pushed the fizz-into-container branch from 1435087 to 3af7e54 Compare January 11, 2023 21:10
@gnoff gnoff force-pushed the fizz-into-container branch from 02ceffe to 4391557 Compare January 12, 2023 21:29
gnoff added 4 commits January 16, 2023 09:39
renderIntoContainer allows you to stream react into a specific DOM node identified by a element ID.

It returns a ReadableStream but differs from renderToReadableStream in that you get access to the stream syncronously.

Since the top level Component gets inserted like a boundary I am calling what would normally be the "Shell" of a React app the Root Boundary when using renderIntoContainer. While the top level Components stream in like a boundary there is not actually a Suspense Boundary wrapper. However it will act like one in that stylesheets will be loaded before the content is revealed in the container Node. If stylehseet loading fails hydration will fall back to client rendering

It allows the passing of fallback bootstrap scripts which are used if the Root Boundary errors.
@gnoff gnoff force-pushed the fizz-into-container branch from 4391557 to 8be28e5 Compare January 16, 2023 17:39
node.remove();
switch (dataset.ri) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Originally the idea was to encode the instruction in the name of the first argument. That's why it was one argument per instruction since it's fewer bytes to do data-rxi="arg0" than "data-ri="x" data-bid="arg0". We should do that instead.

I'm not sure why the instruction attribute wasn't used for arguments previously though.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The escaping of the name like dataset['ri'] is good practice because we should ideally be able to use closure compiler advanced mode and in that case these properties would otherwise be minified. So we've been trying to stick to that as much as possible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sounds good, made them more compact

@@ -108,5 +108,7 @@ export const enableCustomElementPropertySupport = __EXPERIMENTAL__;
export const useModernStrictMode = false;
export const enableFizzExternalRuntime = true;

export const enableFizzIntoContainer = __EXPERIMENTAL__;
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can just turn it on here and they'll use it if they want. Experimental here is more about classic vs modern which is a different dimension where we mostly turn things off experimentally.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

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

Did you consider using writeCompletedRoot for this instead and maybe pass the boundary ID to it. That way you can create a special instruction that applies just to the root and only if a specific boundary ID is used?

@@ -2349,7 +2378,6 @@ function flushCompletedQueues(

// Next we emit any segments of any boundaries that are partially complete
// but not deeply complete.
const partialBoundaries = request.partialBoundaries;
Copy link
Collaborator

Choose a reason for hiding this comment

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

The idea of these is that we should be able to mutate these as we flush if we discover things, decide to outline them, or in the case of partial boundaries, if we flush them as completed boundaries. Even if we mutate the set, we still have the same one here, but maybe it's better to read it.

We've also discovered that we do this pattern a lot which requires more on the stack and worse reuse of registers. So probably shouldn't do it here.

@@ -36,7 +37,14 @@ function createAbortHandler(request: Request, reason: string) {
return () => abort(request, new Error(reason));
}

type Options = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The name of a type is semi-public. Since even if we don't export them, people like @eps1lon have to look at them and give them some name that ends up being part of the export. Therefore changing the name of a type can be a breaking change.

In this case he had to pick some other names:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-dom/server.d.ts#L91

It's arguably too late to change the convention so seems like we should align on those names and going forward really think through what names we give to these and probably explicitly export the type to make that clear.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I usually try to stick to ${ucfirst(methodName)}Options for naming. Gets a bit more tricky when we have shared options but overall I like that the verbose name requires less context (see "? Options" or "? toReadableOptions")

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since we're export flow types and not TS types does a name change here actually ever break anything? I guess for flow users it could.

Should I just export the options now following the existing convention?

export type RenderToReadableStreamOptions = ...
export type RenderIntoContainerOptions = ...

Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like the Options convention in JS is just a hack around named arguments anyway and it doesn't actually have its own nominal structure. So it makes sense that each function gets their own. As we've seen, they can also change independently.

That convention shouldn't necessarily carry over to any other type than "Options" though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This makes sense to me

@@ -305,7 +306,17 @@ export function createRequest(
onShellError: onShellError === undefined ? noop : onShellError,
onFatalError: onFatalError === undefined ? noop : onFatalError,
};
// This segment represents the root fallback.
const rootBoundaryID = getRootBoundaryID(responseState);
Copy link
Collaborator

Choose a reason for hiding this comment

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

responseState isn't really supposed to be where all configuration lives. It represents things that are stateful. We kind of abused it to also store cached things (converted to chunks).

In this case you don't really need to create a response state only to read back the value from it immediately. You can just pass in a SuspenseBoundaryID as the argument to createRequest here to initialize the root boundary.

let maybeRootBoundary = null;
if (rootBoundaryID !== UNINITIALIZED_SUSPENSE_BOUNDARY_ID) {
const fallbackAbortSet: Set<Task> = new Set();
const rootBoundary = createSuspenseBoundary(request, fallbackAbortSet);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This part makes me a bit uncomfortable because it's not a Suspense boundary on the client although it kind of acts like one. We're going to be splitting Suspense boundaries into the Offscreen boundaries for the server and changing how to annotations work and how they have to line up.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think that really matters for this implementation. the fact that there is a suspense boundary here only affects the flushing semantics. We never stream a fallback and there is no boundary marker comments emitted so from a client perspective the fact that we use a boundary here is an unobservable implementation detail

@gnoff
Copy link
Collaborator Author

gnoff commented Jan 17, 2023

Did you consider using writeCompletedRoot for this instead and maybe pass the boundary ID to it. That way you can create a special instruction that applies just to the root and only if a specific boundary ID is used?

Yeah, it's a little tricky. The bootstrap goes first when we are completing the root boundary with styles because we need to ensure the scripts are present before the instruction tries to relocate them. IF we did it after then it's possible we'd miss some bootstrap chunks. But in cases where you aren't using a root boundary or when the root boundary does not depend on styles and can do the swap synchronously we write the bootstrap after the instruction and don't put it in a temporary template.

I could fork the entire writeCompletedBoundaryInstruction and have a writeCompletedRootBoundary instead though. I'll explore that

@gnoff
Copy link
Collaborator Author

gnoff commented Jan 17, 2023

The goal is for the code that does the resolving of options into these to get inline in the code, which then have downstream effects like how the options undefined checks work, but because of how we set these up to today it doesn't happen.

We could instead potentially split this out into separate createResponseState for different variants.

If I understand you correctly we want the createResposneState function to be inlined entirely? What do you mean about how options undefined checks changing based on whether it is inlined or not. Do you mean it would DCE those checks because we statically call the options with undefined?

As for getting it to inline, I tried making a separate createResponseState for renderIntoContainer so that it was only invoked from 1 place. that didn't cause any inlining. Then I tried moving the code that fills the bootstrapChunks into a separate function in case that couldn't be inlined but everything else could but that didn't lead to any inlining.

When I look at closure documentation it seems to suggest that inlining is used during advanced compilation, but I know that isn't true entirely b/c we have inlining in many places today and we're on simple optimizations

@gnoff gnoff force-pushed the fizz-into-container branch 3 times, most recently from fb927b3 to e4fccad Compare January 18, 2023 00:23
@gnoff gnoff force-pushed the fizz-into-container branch from e4fccad to 8e5cd60 Compare January 18, 2023 00:25
Copy link
Contributor

@mofeiZ mofeiZ left a comment

Choose a reason for hiding this comment

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

It took a while to wrap my head around Fizz Server, but this solution makes a lot of sense. Thanks for the comments and explanations here, super useful!

Is this implementation subject to change (I see quite a few other Float features in development) or mostly stable?


(following feedback is regarding style / docs)

It took a while for me to understand fizz server code and reason about these changes (although it could definitely be just me being slow). Request feels like a big ball of mutable state whose semantics are described mostly by imperative code. In the absence of the Request type encoding its invariants or having nested object types, could we add docblocks (comments + typedefs) to document this?
Some examples:

  • mutually exclusive fields
  • fields whose values encode rendering state -- completedRootSegment is only set once, when the first task (the enqueued root segment) completes. null indicates either that the rootSegment errored, or that we have already flushed
  • fields that mutate with / depend on another field. E.g. completedRootSegment and pendingRootTasks

I understand that maintaining the correctness of comments can be a chore (code is self documenting), but just wanted to bring up for discussion - happy to take a stab at writing this if we do want to add.

Comment on lines +168 to +180
const scriptObserverFunctionString = `
const scriptObserver = new MutationObserver(mutations => {
let promises = [];
for (var i = 0; i < mutations.length; i++) {
const addedNodes = mutations[i].addedNodes;
if (addedNodes.length) {
var pendingWork = window.__loadScripts(window);
window.__pendingWork = (window.__pendingWork || Promise.resolve()).then(() => pendingWork);
// We can stop here because loadScripts loads scripts globally so there
// is not additional benefit to calling it for each mutation that added nodes
}
}
});

window.__scriptObserver = scriptObserver;
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

@gnoff I think JSDOM window provides a mocked MutationObserver. Fizz tests still pass when I remove eval(...) and call new window.MutationObserver(...)

function makeScriptObserver(window: JSDOM.DOMWindow) {
  return window.__scriptObserver = new window.MutationObserver(...);
}

clientRenderBoundary(
dataset['bid'],

if (dataset['ix'] != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Question - would we expect / handle any of these instruction functions to throw here? I noticed a try-catch in completeContainer, but couldn't find what might throw in that function. Other instruction functions don't look like they would throw either

We should add a try-catch here if other functions can potentially throw (and React is expected to keep rendering). Inline scripts isolate failures, but the external runtime may skip processing further instructions / MutationRecords if one errors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I ended up removing the throw but not the wrapping try catch. Good call on handling it here though, we probably should not actually throw and just return from completeContainer

// streaming into a container node when a browser would not do that except in very contrived
// circumstances. We should migrate to the new act which infers the streaming location as
// either the document, documentElement, or document.body as appropriate
async function actIntoContainer(callback) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🙌

@mofeiZ
Copy link
Contributor

mofeiZ commented Feb 14, 2023

Another question (not entirely related to renderIntoContainer API).

We current use JSON script tags to describe client-side callbacks / events. These are read by a MutationObserver on the client, which resolves + invokes the actual callback. This allows us to fulfill Binary Transparency requirements (i.e. no inline scripts execution).
e.g.

<script type="application/json" data-special-tag="">{"key": "value}</script>

To use renderIntoContainer with our existing infra, we would want to specify type (i.e. some non executable MIME type) and dataset for bootstrap content and bootstrap fallback content. From my understanding, this would not conflict with React SSR's model -- users should be able to configure bootstrap contents as needed for non-React / above-React parts of their stream / runtime. The changes are also small (adding new types to BootstrapScriptDescriptor)

I'm still new to this codebase though, so I can definitely be missing something. @gnoff @sebmarkbage
do you think this is a valid use case to support?

writeChunk(destination, completeBoundaryScript3a);
// boundaryResources encodes an array literal
if (enableFloat && hasStyleDependencies) {
// We emit the bootstrap chunks into a template container with an id
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a clever solution. Curious - was the decision to emit bootstrap container as bs:[sid] to support multiple React SSR roots within a page?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It does potentially enable that however it's unclear how there would be more than one root streaming into the same document at the same time. I'm sure someone could hack it to make that happen but its not something I was trying to explicitly support. I used the sid primarily to add some entropy to the id to avoid colliding with someone else's bs. the sid has the identifier prefix so in theory you can customize it not to collide if you really needed to use bs somewhere else

@gnoff
Copy link
Collaborator Author

gnoff commented Feb 16, 2023

We current use JSON script tags to describe client-side callbacks / events. These are read by a MutationObserver on the client, which resolves + invokes the actual callback.

I'm hesitant to expand the bootstrap scripts description to allow for arbitrary inline scripts, also at the moment we only allow 1 inline script so it would be either executable or not based on the type but you couldn't have both. I get that this isn't really a problem for Meta given the BT constraints but it doesn't feel like the actual best API, just one that happens to be good enough for the use case for today

Can you share more about the timing of these scripts.

Is it important that their insertion happen in conjunction with the bootstrap phase? If they could go early and then be triggered by the bootstrap then you could just render them as part of the (implied) Root Boundary. They would be in the DOM before the bootstrap and then the bootstrap would kick off the whatever instruction processor needs to run. What sort of timing constraints exist?

@gnoff gnoff closed this Dec 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants