remix-run/react-router · error · Error

SPA Mode: Did you forget to include `<Scripts/>` in your roo

Error message

SPA Mode: Did you forget to include `<Scripts/>` in your root route? Your pre-rendered HTML cannot hydrate without `<Scripts />`.

What it means

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.

Source

Thrown at packages/react-router-dev/vite/plugin.ts:2616

        }

        // Handle document responses (html or spa)
        let html = await response.text();

        if (metadata.type === "spa") {
          if (response.status !== 200) {
            throw new Error(
              `SPA Mode: Received a ${response.status} status code from ` +
                `\`entry.server.tsx\` while prerendering your SPA Fallback HTML file.\n` +
                html,
            );
          }

          if (
            !html.includes("window.__reactRouterContext =") ||
            !html.includes("window.__reactRouterRouteModules =")
          ) {
            throw new Error(
              "SPA Mode: Did you forget to include `<Scripts/>` in your root route? " +
                "Your pre-rendered HTML cannot hydrate without `<Scripts />`.",
            );
          }

          // SPA fallback is written to root regardless of basename
          return [
            {
              path: "/__spa-fallback.html",
              contents: html,
            },
          ];
        }

        // Handle html responses
        let pathname = new URL(request.url).pathname;

        if (redirectStatusCodes.has(response.status)) {

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Open the root route (`app/root.tsx`) and confirm `<Scripts />` is rendered inside `<body>` (typically alongside `<Scripts />`, `<ScrollRestoration />`).
  2. Ensure `<Scripts />` is unconditional in the document layout.
  3. If using a custom `entry.server.tsx`, do not strip `<script>` tags from the rendered HTML.
  4. Rebuild and grep the produced `__spa-fallback.html` for `__reactRouterContext`.

Example fix

// before: root.tsx omits Scripts
export default function Root() {
  return (<html><body><Outlet/></body></html>);
}
// after
import { Scripts, Outlet } from "react-router";
export default function Root() {
  return (
    <html><body><Outlet/><Scripts/></body></html>
  );
}
Defensive patterns

Strategy: validation

Validate before calling

// Statically assert root renders Scripts
import { readFileSync } from "node:fs";
const rootSrc = readFileSync("app/root.tsx", "utf8");
if (!/<Scripts\s*\/?\>/.test(rootSrc)) {
  throw new Error("app/root.tsx must render <Scripts />");
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/f17aedd98e97bea3. Report an issue: GitHub.