{"record":{"id":"f17aedd98e97bea3","repo":"remix-run/react-router","slug":"spa-mode-did-you-forget-to-include-scripts-i","errorCode":null,"errorMessage":"SPA Mode: Did you forget to include `<Scripts/>` in your root route? Your pre-rendered HTML cannot hydrate without `<Scripts />`.","messagePattern":"SPA Mode: Did you forget to include `<Scripts/>` in your root route\\? Your pre-rendered HTML cannot hydrate without `<Scripts />`\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/plugin.ts","lineNumber":2616,"sourceCode":"        }\n\n        // Handle document responses (html or spa)\n        let html = await response.text();\n\n        if (metadata.type === \"spa\") {\n          if (response.status !== 200) {\n            throw new Error(\n              `SPA Mode: Received a ${response.status} status code from ` +\n                `\\`entry.server.tsx\\` while prerendering your SPA Fallback HTML file.\\n` +\n                html,\n            );\n          }\n\n          if (\n            !html.includes(\"window.__reactRouterContext =\") ||\n            !html.includes(\"window.__reactRouterRouteModules =\")\n          ) {\n            throw new Error(\n              \"SPA Mode: Did you forget to include `<Scripts/>` in your root route? \" +\n                \"Your pre-rendered HTML cannot hydrate without `<Scripts />`.\",\n            );\n          }\n\n          // SPA fallback is written to root regardless of basename\n          return [\n            {\n              path: \"/__spa-fallback.html\",\n              contents: html,\n            },\n          ];\n        }\n\n        // Handle html responses\n        let pathname = new URL(request.url).pathname;\n\n        if (redirectStatusCodes.has(response.status)) {","sourceCodeStart":2598,"sourceCodeEnd":2634,"githubUrl":"https://github.com/remix-run/react-router/blob/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/packages/react-router-dev/vite/plugin.ts#L2598-L2634","documentation":"Thrown in `postProcess` when `metadata.type === \"spa\"` and the status is 200 but the rendered HTML is missing the inline `<Scripts/>` output. React Router detects SPA-readiness by checking for `window.__reactRouterContext =` and `window.__reactRouterRouteModules =` in the rendered HTML; their absence means `<Scripts/>` (and therefore the bootstrap payload) is not in the root route, so the SPA cannot hydrate.","triggerScenarios":"Root route's `<html>` document does not render `<Scripts />`; `<Scripts />` is conditionally rendered and skipped in the fallback render; a custom `entry.server.tsx` strips script tags; the root layout was edited to remove `<Scripts />` while experimenting.","commonSituations":"Customizing the root document and forgetting `<Scripts />`; copy-pasting an HTML template from a non-React-Router source; conditionally hiding `<Scripts />` based on env vars that evaluate differently at build time.","solutions":["Open the root route (`app/root.tsx`) and confirm `<Scripts />` is rendered inside `<body>` (typically alongside `<Scripts />`, `<ScrollRestoration />`).","Ensure `<Scripts />` is unconditional in the document layout.","If using a custom `entry.server.tsx`, do not strip `<script>` tags from the rendered HTML.","Rebuild and grep the produced `__spa-fallback.html` for `__reactRouterContext`."],"exampleFix":"// before: root.tsx omits Scripts\nexport default function Root() {\n  return (<html><body><Outlet/></body></html>);\n}\n// after\nimport { Scripts, Outlet } from \"react-router\";\nexport default function Root() {\n  return (\n    <html><body><Outlet/><Scripts/></body></html>\n  );\n}","handlingStrategy":"validation","validationCode":"// Statically assert root renders Scripts\nimport { readFileSync } from \"node:fs\";\nconst rootSrc = readFileSync(\"app/root.tsx\", \"utf8\");\nif (!/<Scripts\\s*\\/?\\>/.test(rootSrc)) {\n  throw new Error(\"app/root.tsx must render <Scripts />\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always render `<Scripts />` in the root document body for SPA mode.","Make `<Scripts />` unconditional (no env-gating).","Add a lint rule or pre-build grep for `<Scripts />` in `app/root.tsx`."],"tags":["spa","ssr-false","scripts","hydration","root-route"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}