remix-run/react-router · error
Module cannot have both a Layout export and a ServerLayout…
Error message
Module cannot have both a Layout export and a ServerLayout export
What it means
In RSC mode a route's layout can be supplied either by the client 'Layout' export or by the server 'ServerLayout' export. The framework-route wrapper resolves them in order — client 'Layout' first — and throws when both are truthy, since rendering would be ambiguous between a client layout and a server layout for the same route.
Solutions
- Keep only 'ServerLayout' when the layout must run on the server
- Keep only 'Layout' when the layout needs state/effects on the client
- Extract shared markup into a component module imported by whichever layout remains
Example fix
// before
export function Layout({ children }) { return <div>{children}</div>; }
export function ServerLayout({ children }) { return <Shell>{children}</Shell>; }
// after
export function ServerLayout({ children }) { return <Shell>{children}</Shell>; } Defensive patterns
Strategy: validation
Validate before calling
const PAIRS = [["Layout", "ServerLayout"]] as const;
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}: export either ${client} or ${server}, not both`);
}
}
} Type guard
function hasLayoutConflict(mod: Record<string, unknown>): boolean {
return Boolean(mod.Layout) && Boolean(mod.ServerLayout);
} Prevention
- Pick one layout variant per route and delete the other during migration
- Keep shared layout markup in a separate imported component
- Let the build-time pair validator (error 47) run in CI
When it happens
Trigger: A route module exporting both 'export function Layout({ children })' and 'export function ServerLayout({ children })' with both truthy.
Common situations: Migrating a layout route to server-side rendering by adding ServerLayout on top of an existing Layout export; merging two example route files during RSC adoption.
Related errors
- Invalid route module exports. The following pairs of…
- Module cannot have both a default export and a…
- Module cannot have both a HydrateFallback export and a…
- Module cannot have both an ErrorBoundary export and a…
- Error splitting route module
AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18).
Data as JSON: /api/errors/2066a2f391b12e3c.
Report an issue: GitHub.
Appendix: source
Thrown at packages/react-router-dev/vite/rsc/virtual-route-config.ts:32
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",
);
}
ErrorBoundary = mod.ErrorBoundary;
} else if ("ServerErrorBoundary" in mod && mod.ServerErrorBoundary) {
ErrorBoundary = mod.ServerErrorBoundary;
}
if ("HydrateFallback" in mod && mod.HydrateFallback) {
if ("ServerHydrateFallback" in mod && mod.ServerHydrateFallback) {
throw new Error(View on GitHub (pinned to 6beaca3952)