Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,42 @@ import {
encodeReply,
setServerCallback,
} from '@vitejs/plugin-rsc/browser'
import * as React from 'react'
import { startTransition, StrictMode } from 'react'
import { hydrateRoot } from 'react-dom/client'
import {
unstable_RSCHydratedRouter as RSCHydratedRouter,
type unstable_RSCPayload as RSCPayload,
unstable_createCallServer as createCallServer,
unstable_getRSCStream as getRSCStream,
unstable_RSCHydratedRouter as RSCHydratedRouter,
type unstable_RSCPayload as RSCServerPayload,
} from 'react-router'

// Create and set the callServer function to support post-hydration server actions.
setServerCallback(
createCallServer({
createFromReadableStream,
encodeReply,
createTemporaryReferenceSet,
encodeReply,
}),
)

createFromReadableStream<RSCPayload>(getRSCStream()).then(
(payload: RSCPayload) => {
React.startTransition(() => {
hydrateRoot(
document,
<React.StrictMode>
<RSCHydratedRouter
createFromReadableStream={createFromReadableStream}
payload={payload}
/>
</React.StrictMode>,
)
})
},
)
// Get and decode the initial server payload
createFromReadableStream<RSCServerPayload>(getRSCStream()).then((payload) => {
startTransition(async () => {
const formState =
payload.type === 'render' ? await payload.formState : undefined

hydrateRoot(
document,
<StrictMode>
<RSCHydratedRouter
createFromReadableStream={createFromReadableStream}
payload={payload}
/>
</StrictMode>,
{
// @ts-expect-error - no types for this yet
formState,
},
)
})
})
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
createTemporaryReferenceSet,
decodeAction,
decodeFormState,
decodeReply,
loadServerAction,
renderToReadableStream,
Expand All @@ -9,23 +10,37 @@ import { unstable_matchRSCServerRequest as matchRSCServerRequest } from 'react-r

import routes from 'virtual:react-router-routes'

export async function fetchServer(request: Request): Promise<Response> {
return await matchRSCServerRequest({
export function fetchServer(request: Request) {
return matchRSCServerRequest({
// Provide the React Server touchpoints.
createTemporaryReferenceSet,
decodeReply,
decodeAction,
decodeFormState,
decodeReply,
loadServerAction,
// The incoming request.
request,
// The app routes.
routes,
generateResponse(match, options) {
return new Response(renderToReadableStream(match.payload, options), {
// Encode the match with the React Server implementation.
generateResponse(match) {
return new Response(renderToReadableStream(match.payload), {
status: match.statusCode,
headers: match.headers,
})
},
})
}

export default async function handler(request: Request) {
// Import the generateHTML function from the client environment
const ssr = await import.meta.viteRsc.loadModule<
typeof import('./entry.ssr')
>('ssr', 'index')

return ssr.generateHTML(request, fetchServer)
}

if (import.meta.hot) {
import.meta.hot.accept()
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr'
import * as ReactDomServer from 'react-dom/server.edge'
import { renderToReadableStream as renderHTMLToReadableStream } from 'react-dom/server.edge'
import {
unstable_RSCStaticRouter as RSCStaticRouter,
unstable_routeRSCServerRequest as routeRSCServerRequest,
unstable_RSCStaticRouter as RSCStaticRouter,
} from 'react-router'

export default async function handler(
export async function generateHTML(
request: Request,
fetchServer: (request: Request) => Promise<Response>,
): Promise<Response> {
const bootstrapScriptContent =
await import.meta.viteRsc.loadBootstrapScriptContent('index')
return routeRSCServerRequest({
return await routeRSCServerRequest({
// The incoming request.
request,
// How to call the React Server.
fetchServer,
createFromReadableStream: (body) => createFromReadableStream(body),
renderHTML(getPayload) {
return ReactDomServer.renderToReadableStream(
// Provide the React Server touchpoints.
createFromReadableStream,
// Render the router to HTML.
async renderHTML(getPayload) {
const payload = await getPayload()
const formState =
payload.type === 'render' ? await payload.formState : undefined

const bootstrapScriptContent =
await import.meta.viteRsc.loadBootstrapScriptContent('index')

return await renderHTMLToReadableStream(
<RSCStaticRouter getPayload={getPayload} />,
{
bootstrapScriptContent,
// @ts-expect-error - no types for this yet
formState,
},
)
},
})
}

export default async function handler(
request: Request,
fetchServer: (request: Request) => Promise<Response>,
): Promise<Response> {
return generateHTML(request, fetchServer)
}
44 changes: 22 additions & 22 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.