remix-run/react-router · error · Error
Module cannot have both a default export and a ServerCompone
Error message
Module cannot have both a default export and a ServerComponent export
What it means
Runtime guard inside the generated frameworkRoute() wrapper (virtual-route-config.ts) for RSC Framework Mode. A route module may export either a client `default` component OR a `ServerComponent`, but not both — the runtime cannot decide which to render on each side. This check executes in the browser/server bundle when the route module loads.
Source
Thrown at packages/react-router-dev/vite/rsc/virtual-route-config.ts:24
export function createVirtualRouteConfig({
appDirectory,
routeConfig,
}: {
appDirectory: string;
routeConfig: RouteConfigEntry[];
}): { code: string; routeIdByFile: Map<string, string> } {
let routeIdByFile = new Map<string, string>();
let code = js`import * as React from "react";
function frameworkRoute(lazy) {
return async () => {
const mod = await lazy();
let Component;
let Layout;
let ErrorBoundary;
let HydrateFallback;
if ("default" in mod && mod.default) {
if ("ServerComponent" in mod && mod.ServerComponent) {
throw new Error("Module cannot have both a default export and a ServerComponent export");
}
Component = mod.default;
} else if ("ServerComponent" in mod && mod.ServerComponent) {
Component = mod.ServerComponent;
}
if ("Layout" in mod && mod.Layout) {
if ("ServerLayout" in mod && mod.ServerLayout) {
throw new Error("Module cannot have both a Layout export and a ServerLayout export");
}
Layout = mod.Layout;
} else if ("ServerLayout" in mod && mod.ServerLayout) {
Layout = mod.ServerLayout;
}
if ("ErrorBoundary" in mod && mod.ErrorBoundary) {
if ("ServerErrorBoundary" in mod && mod.ServerErrorBoundary) {
throw new Error(
"Module cannot have both an ErrorBoundary export and a ServerErrorBoundary export",
);View on GitHub (pinned to 1fd704a7da)
Solutions
- In the offending route module, choose ONE: keep `export default` for a client component, or rename it to `export function ServerComponent` for a server component.
- Use `pnpm react-router typegen` to surface the conflict at compile time too (it shares the MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS table).
- Re-run dev/build after editing to confirm the runtime guard no longer fires.
Example fix
// app/routes/foo.tsx
// before
export default function Foo() { return <p>client</p>; }
export function ServerComponent() { return <p>server</p>; }
// after (server component only)
export function ServerComponent() { return <p>server</p>; } Defensive patterns
Strategy: validation
Validate before calling
// build-time check: scan route modules for the conflicting pair
import { readFileSync } from 'node:fs';
const src = readFileSync(routeFile, 'utf8');
const hasDefault = /export\s+default\s/.test(src);
const hasServerComponent = /export\s+(async\s+)?function\s+ServerComponent\b/.test(src);
if (hasDefault && hasServerComponent) throw new Error(`${routeFile}: cannot export both default and ServerComponent`); Type guard
function moduleExportsOnlyOneComponent(mod: Record<string, unknown>): boolean {
return !mod.default || !mod.ServerComponent;
} Prevention
- Adopt a lint rule banning both exports in one file.
- Run typegen after refactors — it flags these via MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS.
When it happens
Trigger: A single route module file (e.g. app/routes/foo.tsx) has both `export default function Foo()` and `export function ServerComponent()`. The frameworkRoute wrapper's `if ('default' in mod && mod.default)` branch then checks for ServerComponent and throws.
Common situations: Refactoring a client component into an RSC and forgetting to remove the default export. Copy-pasting a hybrid example. Tooling (codemods) that auto-adds ServerComponent without removing default.
Related errors
- 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
- Invalid route module exports. The following pairs of exports
- ${configResult.error}
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/a721cd6ac55d6210.
Report an issue: GitHub.