remix-run/react-router · error · Error
Module cannot have both a Layout export and a ServerLayout e
Error message
Module cannot have both a Layout export and a ServerLayout export
What it means
Runtime guard in frameworkRoute() (virtual-route-config.ts): a route module cannot export both `Layout` (client) and `ServerLayout` (server). The wrapper picks one Layout slot; exporting both is ambiguous. Thrown at module-load time inside the lazy route resolver.
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 1fd704a7da)
Solutions
- Keep only one Layout export per module: `Layout` for client-rendered layouts, or `ServerLayout` for server-rendered layouts.
- If you need both behaviors, split into two route modules or use a shared child component imported by each.
- Regenerate types to get the build-time duplicate check.
Example fix
// app/routes/_layout.tsx
// before
export function Layout({ children }) { return <div className="client">{children}</div>; }
export function ServerLayout({ children }) { return <div className="server">{children}</div>; }
// after (server layout only)
export function ServerLayout({ children }) { return <div className="server">{children}</div>; } Defensive patterns
Strategy: validation
Validate before calling
const src = readFileSync(layoutFile, 'utf8');
const hasLayout = /export\s+(async\s+)?function\s+Layout\b/.test(src);
const hasServerLayout = /export\s+(async\s+)?function\s+ServerLayout\b/.test(src);
if (hasLayout && hasServerLayout) throw new Error(`${layoutFile}: cannot export both Layout and ServerLayout`); Type guard
function moduleExportsOnlyOneLayout(mod: Record<string, unknown>): boolean {
return !mod.Layout || !mod.ServerLayout;
} Prevention
- Pick one layout export per module; document the choice in a comment.
- Use typegen to catch duplicates before runtime.
When it happens
Trigger: A layout route module (e.g. app/routes/_layout.tsx) exports both `Layout` and `ServerLayout`. The `if ('Layout' in mod && mod.Layout)` branch detects the coexistence and throws.
Common situations: Splitting a layout into client/server variants and leaving both exports. Migrating a Framework Mode layout to RSC Framework Mode without removing the client Layout.
Related errors
- Module cannot have both a default export and a ServerCompone
- 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
- 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/2066a2f391b12e3c.
Report an issue: GitHub.