remix-run/react-router · error · Error
Invalid route module exports. The following pairs of exports
Error message
Invalid route module exports. The following pairs of exports are mutually exclusive and cannot be exported from the same module:
${errors.map(([clientExport, serverExport]) => `- ${clientExport} and ${serverExport}`).join("\n")} What it means
Build-time validation in virtual-route-modules.ts: the MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS map lists four client/server pairs — (default, ServerComponent), (Layout, ServerLayout), (ErrorBoundary, ServerErrorBoundary), (HydrateFallback, ServerHydrateFallback). validateRouteModuleExports collects every pair where BOTH members appear in the module's export list and throws a single aggregated error listing them. This is the compile-time counterpart of the runtime guards (errors 47–50).
Source
Thrown at packages/react-router-dev/vite/rsc/virtual-route-modules.ts:478
const MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS = new Map([
["ErrorBoundary", "ServerErrorBoundary"],
["HydrateFallback", "ServerHydrateFallback"],
["Layout", "ServerLayout"],
["default", "ServerComponent"],
]);
function validateRouteModuleExports(toValidate: string[]) {
let errors: [string, string][] = [];
for (let [clientExport, serverExport] of MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS) {
if (
toValidate.includes(clientExport) &&
toValidate.includes(serverExport)
) {
errors.push([clientExport, serverExport]);
}
}
if (errors.length > 0) {
throw new Error(
`Invalid route module exports. The following pairs of exports are mutually exclusive and cannot be exported from the same module:\n` +
errors
.map(
([clientExport, serverExport]) =>
`- ${clientExport} and ${serverExport}`,
)
.join("\n"),
);
}
}
type RouteChunks = ReturnType<typeof _detectRouteChunks>;
function detectRouteChunks(
cache: Cache,
id: string,
code: string,
isRootRouteModule: boolean,View on GitHub (pinned to 1fd704a7da)
Solutions
- Read the error body — each line names the conflicting pair (e.g. `- default and ServerComponent`).
- For each listed pair, remove one of the two exports from that module (see fixes for errors 47–50).
- Re-run the build; the validator re-scans all route modules and reports any remaining pairs.
- Add an ESLint rule or pre-commit grep to prevent re-introducing both exports.
Example fix
// build error lists: `- default and ServerComponent`, `- Layout and ServerLayout`
// fix: in each flagged file, delete the client export when a server export exists
// app/routes/foo.tsx — before
export default function Foo() { /* client */ }
export function ServerComponent() { /* server */ }
export function Layout({ children }) { /* client */ }
export function ServerLayout({ children }) { /* server */ }
// after
export function ServerComponent() { /* server */ }
export function ServerLayout({ children }) { /* server */ } Defensive patterns
Strategy: validation
Validate before calling
const MUTUALLY_EXCLUSIVE = [['default','ServerComponent'],['Layout','ServerLayout'],['ErrorBoundary','ServerErrorBoundary'],['HydrateFallback','ServerHydrateFallback']];
function detectConflicts(exports: string[]): [string,string][] {
return MUTUALLY_EXCLUSIVE.filter(([a,b]) => exports.includes(a) && exports.includes(b));
}
// run detectConflicts over each route module's static export list before build Type guard
function routeModuleIsValid(exports: string[]): boolean {
const pairs = [['default','ServerComponent'],['Layout','ServerLayout'],['ErrorBoundary','ServerErrorBoundary'],['HydrateFallback','ServerHydrateFallback']];
return pairs.every(([a,b]) => !(exports.includes(a) && exports.includes(b)));
} Prevention
- Add a CI step that scans app/routes/** for mutually exclusive export pairs.
- Adopt an ESLint custom rule mirroring MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS.
- Run typegen in CI; it surfaces the same conflicts at compile time.
When it happens
Trigger: During RSC Framework Mode build/vite-dev, a route module is analyzed and its static export names are passed to validateRouteModuleExports. If any mutually exclusive pair co-occurs, the error lists all offending pairs.
Common situations: Same as 47–50 but caught at build time: refactoring route modules without removing the superseded export. Bulk migration from Framework Mode to RSC Framework Mode leaving duplicates across many files.
Related errors
- Module cannot have both a default export and a ServerCompone
- Module cannot have both a Layout export and a ServerLayout e
- Module cannot have both an ErrorBoundary export and a Server
- Module cannot have both a HydrateFallback export and a Serve
- Error splitting route module: ${id} ${invalidChunks.map((na
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/164b58e9b147ba60.
Report an issue: GitHub.