Conversation
… assembleHtmlStreams to inject the correct preamble.
…`bootstrapModules` option to `renderToReadableStream`.
…sing `bootstrapModules` API.
Does this mean there's a chance of IDs colliding? Could |
Do you mean collisions between RSC and SSR/Client ids? If so, good question. React generates ids with different prefixes for the two cases, so we're safe there. |
Deploying redwood-sdk-docs with
|
| Latest commit: |
f15063f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://32ca27b6.redwood-sdk-docs.pages.dev |
| Branch Preview URL: | https://radix-ui.redwood-sdk-docs.pages.dev |
I mean, if you have two concurrent renders happening on the server, could a |
Ah yes, good catch, I'll add different id prefixes to solve that, thanks! |
…ent environments.
…en testing stack.
Context: The Previous Rendering Architecture
Previously, the framework used a single, nested rendering pass on the server to produce the initial HTML document. The user's
<Document>component (containing the<html>,<head>, etc.) was rendered using React's standard Server-Side Rendering (SSR). As part of this same render, the framework would resolve the React Server Component (RSC) payload for the page and render its contents into the document shell.Problem: Non-Deterministic
useIdGenerationThis approach created a hydration mismatch for client components that rely on
React.useId(such as those in Radix UI). React's hydration foruseIdrequires deterministic rendering—the sequence of hook calls that generate IDs must be identical on the server and the client.Our single-pass architecture broke this determinism. The server would first traverse and render the components within the
<Document>shell, advancing React's internaluseIdcounter. Only then would it proceed to render the actual application components. The client, however, only hydrates the application content within the document, starting with a freshuseIdcounter. This discrepancy meant the server was performing extra rendering work that the client was unaware of, leading to a mismatch in the final IDs (e.g., server_R_76_vs. client_r_0_). This caused React to discard the server-rendered DOM, breaking interactivity and negating the benefits of SSR.Solution: Isolate, Render, and Stitch
The solution was to re-architect the server-side rendering pipeline to enforce context isolation. The new "Nested Renders with Stream Stitching" model works as follows:
appHtmlStream). This guarantees it renders in a clean context with a freshuseIdcounter.<Document>shell, which generates another HTML stream (documentHtmlStream) containing a placeholder comment.This approach guarantees that the application content is rendered in an isolated context, ensuring the
useIdsequence generated on the server is identical to the one generated on the client during hydration, while at the same time ensuring streaming isn't blocked for both the document and app RSC renders.An important secondary benefit of this change is that the user-defined
<Document>is now a true React Server Component. This aligns with developer expectations and unlocks the full power of the RSC paradigm (e.g., usingasync/awaitfor data fetching, accessing server-only APIs) directly within the document shell, which was not possible before. The full details of this new architecture are captured in the updated Hybrid Rendering documentation.