React Streaming example is different than the React recomendation
This is more of a clarification question (or a series of them) than it is an issue.
I was reading through the React + TS + Streaming example that you have and it's wildly straight forward. However, I was reading React's documentation and the suggested recommendation differs to a large extent from what's in the example.
https://react.dev/reference/react-dom/server/renderToPipeableStream#rendering-a-react-tree-as-html-to-a-nodejs-stream
The clear difference is that in your example you're importing a HTML template that has a comment to be replaced with the SSR react tree, however React bolds this text in their docs
Along with the root component, you need to provide a list of
bootstrap <script> paths. Your root component should return the entire document including the root<html>tag.
Below is your example where the manifest during the Dev build get's inserted into the template.
template = await fs.readFile('./index.html', 'utf-8')
template = await vite.transformIndexHtml(url, template)
Below is React's example. The <App /> in this case would be the stuff in the entry-server.tsx and then the options would be the options that are fed to renderToPipeableStream.
import { renderToPipeableStream } from 'react-dom/server';
// The route handler syntax depends on your backend framework
app.use('/', (request, response) => {
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
});
At this point, I haven't found a way to adapt your example with what React suggests for the sole reason of not being able to correctly find the in memory built / compiled assets.
Is there a way to do what React is suggesting where you insert the built assets URLs into either the bootstrapScripts or the bootstrapModules option fields to replace the entire html node in the document? Or is this the recommendation that Vite suggests?
The only other thing I can really think of to make this happen is to create a Virtual Module to be able to read the manifest files in the build, but again that would be reliant on being able to get a manifest during the development server being run.
Thanks!
The React API has the assumption that <App /> is the root HTML that renders <html> and everything else. The templates setup here is different where index.html dictates that instead, the <App /> is only partial. So what the React API is suggesting doesn't really fit for us. We're combining our index.html with React's stream in the onShellReady hook to resolve this, and as a result because the scripts are already included the HTML:
- In dev, it's
<script type="module" src="/src/entry-client.tsx"></script> - In build, it's
<script type="module" crossorigin src="/assets/index-vvHj4PnF.js"></script>
You don't really need to tell React anymore to append any other scripts as we've handled that ourselves. So in this case, you can ignore its suggestion.
Excellent, that's some great guidance. I figured that Vite had it's own conventions that didn't really work with the model that React had. I mean, it's close but apparently Vite takes care of that for you.
Would that same advice pertain to the recommendation that React has for adding / inserting critical CSS into the head of the document before the streaming response resolves?
https://react.dev/reference/react-dom/server/renderToPipeableStream#reading-css-and-js-asset-paths-from-the-build-output
The reason I ask is that I'm running into some issues where some critical CSS is only added to the document after the streaming response finishes which results in some flashing.
Since Vite handles CSS out of the box, is there a way to kind of await entire streaming response when the server first starts up? I'd imagine that this isn't a much of a problem during the the built assets since the manifest is being created.
Are you seeing the flash in dev or build? In build it shouldn't be flashing as the CSS are in the head. In dev, it's kinda expected at the moment as Vite treats CSS as normal JS modules and only include them when imported. See https://github.com/vitejs/vite/issues/16515
Going to close this for now to clean up the issues list, but feel free to follow-up with any questions if you have.
Appreciate all of the context! I've worked through the issues I have albeit a little more conceptual than I thought.
I think the breakdown in the flow of understanding for me was that all of scripts, links, stylesheets, etc... needed to be managed independently of the implementation when building for production.
For instance, vite.transformIndexHtml takes care of what is needed when exclusively developing. When you're running express locally with node, express handles the request and the transformIndexHtml takes care of your script parsing and injection. However, when you move to PROD and you don't want to use express or you're trying to deploy your SSR application to another target (let's say a edge environment) you need to handle the request, the script, tags, and stylesheets yourself from the output vite manifest.
I was thinking that there was some magical way that vite recommended to deploy to production but it turns out it's very use case dependent. I also think I inadvertently created an SSR framework since I was handling code splitting, route splitting, and stylesheet conventions.
you did the right thing in closing this. I'm in complete understanding right now. Thanks!