remix-run/react-router · error

Invalid route module exports. The following pairs of…

Error message

Invalid route module exports. The following pairs of exports are mutually exclusive and cannot be exported from the same module:
- ${clientExport} and ${serverExport}

What it means

When the RSC Vite plugin materializes a route module, validateRouteModuleExports checks the module's export list against MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS — the pairs default/ServerComponent, Layout/ServerLayout, ErrorBoundary/ServerErrorBoundary, and HydrateFallback/ServerHydrateFallback. If any pair is co-exported, the build fails listing every offending pair. This is the static, build-time counterpart of the runtime wrapper checks, so the error appears during 'react-router build'/'react-router dev' instead of at render time.

Solutions

  1. For each pair listed in the message, delete one of the two exports (keep the Server variant only when the logic must run server-side)
  2. Re-run the build to confirm no other pairs remain — the error lists all violations at once
  3. Move shared code used by both variants into a separate module and import it

Example fix

// before — app/routes/post.tsx
export default function Route() { /* client component */ }
export function ServerComponent() { /* server component */ }

// after
export function ServerComponent() { /* single server component */ }
Defensive patterns

Strategy: validation

Validate before calling

// mirror of the library's MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS map
const PAIRS: [string, string][] = [
  ["ErrorBoundary", "ServerErrorBoundary"],
  ["HydrateFallback", "ServerHydrateFallback"],
  ["Layout", "ServerLayout"],
  ["default", "ServerComponent"],
];
for (const file of routeFiles) {
  const mod: Record<string, unknown> = await import(pathToFileURL(file).href);
  for (const [client, server] of PAIRS) {
    if (mod[client] && mod[server]) {
      throw new Error(`${file}: ${client} and ${server} are mutually exclusive`);
    }
  }
}

Type guard

function findExclusiveExportPairs(exportNames: string[]): [string, string][] {
  const pairs: [string, string][] = [
    ["ErrorBoundary", "ServerErrorBoundary"],
    ["HydrateFallback", "ServerHydrateFallback"],
    ["Layout", "ServerLayout"],
    ["default", "ServerComponent"],
  ];
  return pairs.filter(([c, s]) => exportNames.includes(c) && exportNames.includes(s));
}

Prevention

When it happens

Trigger: A route module exporting both members of any pair, e.g. both 'default' and 'ServerComponent', or both 'HydrateFallback' and 'ServerHydrateFallback'; multiple violations are all listed in one error.

Common situations: Adopting RSC on an existing Framework-mode app: the old client exports are still present while server variants get added; generated or scaffolded routes that bundle both variants; merging example code during migration.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/21830fb56205a6fe. Report an issue: GitHub.

Appendix: 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 6beaca3952)