{"record":{"id":"265c7bb4cf3c1245","repo":"remix-run/react-router","slug":"module-cannot-have-both-a-hydratefallback-export-a","errorCode":null,"errorMessage":"Module cannot have both a HydrateFallback export and a ServerHydrateFallback export","messagePattern":"Module cannot have both a HydrateFallback export and a ServerHydrateFallback export","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/rsc/virtual-route-config.ts","lineNumber":50,"sourceCode":"        throw new Error(\"Module cannot have both a Layout export and a ServerLayout export\");\n      }\n      Layout = mod.Layout;\n    } else if (\"ServerLayout\" in mod && mod.ServerLayout) {\n      Layout = mod.ServerLayout;\n    }\n    if (\"ErrorBoundary\" in mod && mod.ErrorBoundary) {\n      if (\"ServerErrorBoundary\" in mod && mod.ServerErrorBoundary) {\n        throw new Error(\n          \"Module cannot have both an ErrorBoundary export and a ServerErrorBoundary export\",\n        );\n      }\n      ErrorBoundary = mod.ErrorBoundary;\n    } else if (\"ServerErrorBoundary\" in mod && mod.ServerErrorBoundary) {\n      ErrorBoundary = mod.ServerErrorBoundary;\n    }\n    if (\"HydrateFallback\" in mod && mod.HydrateFallback) {\n      if (\"ServerHydrateFallback\" in mod && mod.ServerHydrateFallback) {\n        throw new Error(\n          \"Module cannot have both a HydrateFallback export and a ServerHydrateFallback export\",\n        );\n      }\n      HydrateFallback = mod.HydrateFallback;\n    } else if (\"ServerHydrateFallback\" in mod && mod.ServerHydrateFallback) {\n      HydrateFallback = mod.ServerHydrateFallback;\n    }\n\n    const {\n      action,\n      clientAction,\n      clientLoader,\n      clientMiddleware,\n      handle,\n      headers,\n      links,\n      loader,\n      meta,","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/vite/rsc/virtual-route-config.ts#L32-L68","documentation":"In RSC mode a route's loading UI can come from the client 'HydrateFallback' export or the server 'ServerHydrateFallback' export, never both. The framework-route wrapper picks the client variant first but throws when both are truthy, because which fallback renders during hydration would be ambiguous.","triggerScenarios":"A route module exporting both 'export function HydrateFallback()' and 'export function ServerHydrateFallback()' with both truthy.","commonSituations":"Adding a server-rendered fallback to a route that already declares a client HydrateFallback (common when routes use clientLoader and the fallback was later moved server-side).","solutions":["Keep only 'ServerHydrateFallback' for a server-rendered fallback","Keep only 'HydrateFallback' for a client-only fallback","Share fallback markup via a separate imported component"],"exampleFix":"// before\nexport function HydrateFallback() { return <Spinner />; }\nexport function ServerHydrateFallback() { return <ShellSpinner />; }\n\n// after\nexport function HydrateFallback() { return <Spinner />; }","handlingStrategy":"validation","validationCode":"const PAIRS = [[\"HydrateFallback\", \"ServerHydrateFallback\"]] as const;\nfor (const file of routeFiles) {\n  const mod: Record<string, unknown> = await import(pathToFileURL(file).href);\n  for (const [client, server] of PAIRS) {\n    if (mod[client] && mod[server]) {\n      throw new Error(`${file}: export either ${client} or ${server}, not both`);\n    }\n  }\n}","typeGuard":"function hasHydrateFallbackConflict(mod: Record<string, unknown>): boolean {\n  return Boolean(mod.HydrateFallback) && Boolean(mod.ServerHydrateFallback);\n}","tryCatchPattern":null,"preventionTips":["Keep exactly one fallback export per route module","When adding server fallbacks, remove the client fallback in the same change","Run the RSC build in CI to catch the pair statically"],"tags":["rsc","route-module","exports","hydrate-fallback","naming"],"backgroundTag":"mutually-exclusive-exports","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}