import { isbot } from 'isbot' import { renderToReadableStream } from 'react-dom/server' import type { AppLoadContext, EntryContext } from 'react-router' import { ServerRouter } from 'react-router' export default async function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, routerContext: EntryContext, _loadContext: AppLoadContext ) { let shellRendered = false const userAgent = request.headers.get('user-agent') const body = await renderToReadableStream( , { onError(error: unknown) { responseStatusCode = 500 // Log streaming rendering errors from inside the shell. Don't log // errors encountered during initial shell rendering since they'll // reject and get logged in handleDocumentRequest. if (shellRendered) { console.error(error) } } } ) shellRendered = true // Ensure requests from bots and SPA Mode renders wait for all content to load before responding // https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation if ((userAgent && isbot(userAgent)) || routerContext.isSpaMode) { await body.allReady } responseHeaders.set('Content-Type', 'text/html') return new Response(body, { headers: responseHeaders, status: responseStatusCode }) } // https://reactrouter.com/how-to/suspense#timeouts export const streamTimeout = 6_000