{"record":{"id":"c4b04bdd9eb0c110","repo":"remix-run/react-router","slug":"invalid-route-exports-found-when-prerendering-with","errorCode":null,"errorMessage":"Invalid route exports found when prerendering with `ssr:false`","messagePattern":"Invalid route exports found when prerendering with `ssr:false`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/plugin.ts","lineNumber":3168,"sourceCode":"      while (parentRoute && parentRoute.id !== \"root\") {\n        if (parentRoute.hasLoader && !parentRoute.hasClientLoader) {\n          errors.push(\n            `Prerender: 1 invalid route export in \\`${parentRoute.id}\\` when ` +\n              \"pre-rendering with `ssr:false`: `loader`. \" +\n              \"See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information.\",\n          );\n        }\n        parentRoute =\n          parentRoute.parentId && parentRoute.parentId !== \"root\"\n            ? manifest.routes[parentRoute.parentId]\n            : null;\n      }\n    }\n  }\n\n  if (errors.length > 0) {\n    viteConfig.logger.error(colors.red(errors.join(\"\\n\")));\n    throw new Error(\n      \"Invalid route exports found when prerendering with `ssr:false`\",\n    );\n  }\n}\n\nfunction getAddressableRoutes(routes: RouteManifest): RouteManifestEntry[] {\n  let nonAddressableIds = new Set<string>();\n\n  for (let id in routes) {\n    let route = routes[id];\n\n    // We omit the parent route of index routes since the index route takes ownership of its parent's path\n    if (route.index) {\n      invariant(\n        route.parentId,\n        `Expected index route \"${route.id}\" to have \"parentId\" set`,\n      );\n      nonAddressableIds.add(route.parentId);","sourceCodeStart":3150,"sourceCodeEnd":3186,"githubUrl":"https://github.com/remix-run/react-router/blob/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/packages/react-router-dev/vite/plugin.ts#L3150-L3186","documentation":"Terminal throw after `validateSsrFalseExports` collects per-route error strings. With `ssr:false`, the only SSR-capable exports allowed are those on routes that are actually prerendered; `headers`/`action` are never allowed (no server at runtime), and `loader` is allowed only on routes matched by a `prerender` path (or with a `clientLoader`). The per-route errors are logged first, then this aggregate throw halts the build.","triggerScenarios":"Setting `ssr:false` while a route exports `action` (no runtime server exists to call it); a route exports `loader` but is not covered by any `prerender` path and has no `clientLoader`; a parent layout route exports `loader` without `clientLoader` and is not on a prerendered branch.","commonSituations":"Migrating an SSR app to SPA/prerender mode without auditing exports; adding a new route with `action` to an `ssr:false` project; using a parent loader but only prerendering a leaf that doesn't pull it in.","solutions":["Read the logged per-route errors immediately preceding the throw to find the offending `routeId` and export.","Remove `headers`/`action` exports from routes when running `ssr:false`.","For routes with `loader`: add a `clientLoader`, or include the route's path in `prerender`, or remove the `loader`.","For parent loaders failing: ensure the branch is prerendered or convert to `clientLoader`."],"exampleFix":"// before: route has action under ssr:false\nexport const action = async () => { ... };\n// after: drop server-only action, use clientLoader/FORM\nexport const clientAction = async () => { ... };","handlingStrategy":"validation","validationCode":"// Pre-build check: scan routes for forbidden ssr:false exports\nimport { parse } from \"es-module-lexer\";\nconst FORBIDDEN = [\"action\", \"headers\"];\nfor (const [id, file] of Object.entries(routes)) {\n  const [imports, exports] = parse(readFileSync(file, \"utf8\"));\n  const bad = exports.filter(e => FORBIDDEN.includes(e));\n  if (bad.length) console.warn(`${id} exports ${bad.join(\",\")} under ssr:false`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Under `ssr:false`, never export `action` or `headers`.","Only export `loader` on routes covered by `prerender`, or pair it with a `clientLoader`.","Audit parent layout loaders when switching to `ssr:false`."],"tags":["ssr-false","prerender","route-exports","action","loader","build"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}