import { PassThrough } from 'node:stream'; import { createReadableStreamFromReadable } from '@react-router/node'; import { isbot } from 'isbot'; import type { RenderToPipeableStreamOptions } from 'react-dom/server'; import { renderToPipeableStream } from 'react-dom/server'; import type { AppLoadContext, EntryContext } from 'react-router'; import { ServerRouter } from 'react-router'; export const streamTimeout = 5_000; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, routerContext: EntryContext, _loadContext: AppLoadContext, ) { return new Promise((resolve, reject) => { let shellRendered = false; const userAgent = request.headers.get('user-agent'); const readyOption: keyof RenderToPipeableStreamOptions = (userAgent && isbot(userAgent)) || routerContext.isSpaMode ? 'onAllReady' : 'onShellReady'; const { pipe, abort } = renderToPipeableStream(, { [readyOption]() { shellRendered = true; const body = new PassThrough(); const stream = createReadableStreamFromReadable(body); responseHeaders.set('Content-Type', 'text/html'); resolve( new Response(stream, { headers: responseHeaders, status: responseStatusCode, }), ); pipe(body); }, onShellError(error: unknown) { reject(error); }, onError(error: unknown) { responseStatusCode = 500; if (shellRendered) { console.error(error); } }, }); setTimeout(abort, streamTimeout + 1000); }); }