React 19 SSR Fails In Production Mode, But Works In Development #11025
Closed
Description
What version of Bun is running?
1.1.8
What platform is your computer?
Darwin 23.4.0 arm64 arm
What steps can reproduce the bug?
- Build a react 19 application and statically server the files
- Set node env to development mode
- Use render to renderToReadableStream to server the application
import { renderToReadableStream } from "react-dom/server";
await renderToReadableStream(element, {
signal: request.signal,
bootstrapScriptContent: [`_INIT_PATH=${JSON.stringify(pathname)}`, `_ROUTES=${JSON.stringify(routes)}`, `_PROPS=${JSON.stringify(props)}`].filter(Boolean).join(";"),
bootstrapModules: [`${routes["/hydrate"]}`],
identifierPrefix: "app",
});
- set up the entry point to the application for the client
const root = hydrateRoot(document, <App />);
- start the application and visit the page
- At this point in development mode everything works as normal
How to recreate the error
- Set NODE_ENV to production and follow steps 1-6 and you'll receive
{"name":"Error","message":"Objects are not valid as a React child (found: object with keys {$$typeof, type, key, ref, props, _owner}). If you meant to render a collection of children, use an array instead."}
What is the expected behavior?
The expected behavior is that in production mode , the react 19 application would successfully render with bun renderToReadableStream
What do you see instead?
Instead in production the hydration will fail
Additional information
One way to get the code working is to moneky patch the following code out
react-dom-server.bun.production.js
throw Error(
"Objects are not valid as a React child (found: " +
("[object Object]" === childIndex ? "object with keys {" + Object.keys(node$jscomp$0).join(", ") + "}" : childIndex) +
"). If you meant to render a collection of children, use an array instead.",
);
}
however by doing so styles will not load correctly when passing a style from the server
export const app = new Elysia({ prefix: "" })
.get("/home", async (ctx) => {
const Module = await import("../app/home");
const css = await generate(ctx.path, config.app.dir);
const props = {
meta: {
title: "",
description: "",
},
style: css,
};
const stream = await renderToStream(
<Shell {...props}>
<script src="/loader.js" type="module" />
<Module.default {...props} />
{/* <style precedence="high">{css}</style> */}
</Shell>,
props,
ctx.request,
);
return new Response(stream, {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
})
I got around this by passing the styles as a prop and using the
<style></style> tag in the component itself.Typically
{/* <style precedence="high">{css}</style> */}
would work in development mode