{"record":{"id":"a4fb29491beacf79","repo":"remix-run/react-router","slug":"error-splitting-route-module-id-name-th","errorCode":null,"errorMessage":"Error splitting route module: ${id}\n\n- ${name}\n\nThis export could not be split into its own chunk because it shares code with other exports. You should extract any shared code into its own module and then import it within the route module.","messagePattern":"Error splitting route module: (.+?)\n\n- (.+?)\n\nThis export could not be split into its own chunk because it shares code with other exports\\. You should extract any shared code into its own module and then import it within the route module\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/plugin.ts","lineNumber":3356,"sourceCode":"  ctx: ReactRouterPluginContext;\n  id: string;\n  valid: Record<Exclude<RouteChunkName, \"main\">, boolean>;\n}): void {\n  if (isRootRouteModuleId(ctx, id)) {\n    return;\n  }\n\n  let invalidChunks = Object.entries(valid)\n    .filter(([_, isValid]) => !isValid)\n    .map(([chunkName]) => chunkName);\n\n  if (invalidChunks.length === 0) {\n    return;\n  }\n\n  let plural = invalidChunks.length > 1;\n\n  throw new Error(\n    [\n      `Error splitting route module: ${normalizeRelativeFilePath(\n        id,\n        ctx.reactRouterConfig,\n      )}`,\n\n      invalidChunks.map((name) => `- ${name}`).join(\"\\n\"),\n\n      `${plural ? \"These exports\" : \"This export\"} could not be split into ${\n        plural ? \"their own chunks\" : \"its own chunk\"\n      } because ${\n        plural ? \"they share\" : \"it shares\"\n      } code with other exports. You should extract any shared code into its own module and then import it within the route module.`,\n    ].join(\"\\n\\n\"),\n  );\n}\n\nexport async function cleanBuildDirectory(","sourceCodeStart":3338,"sourceCodeEnd":3374,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/vite/plugin.ts#L3338-L3374","documentation":"With `splitRouteModules` enabled, React Router tries to split a route module into separate chunks (clientLoader, clientAction, clientMiddleware, HydrateFallback) so navigation only downloads the code it needs. After splitting, it validates each generated chunk; if an export shares module-level code with others (helpers, constants, React components used by both the main module and a split export), the split is unsafe and the build fails, telling you exactly which exports could not be isolated.","triggerScenarios":"`splitRouteModules: true` (or a config enabling it) where `clientLoader` references a helper also used by the component in the same file; a `HydrateFallback` component and the default export share a constants/functions defined at module scope; module-side effects or shared variables spanning exports.","commonSituations":"Adopting the route-module chunk splitting optimization on existing fat route files; single-file routes that colocate API helpers, types-instantiated at runtime, and UI; enabling the option globally and hitting one legacy route.","solutions":["Extract code shared between the default export and split exports (clientLoader/clientAction/HydrateFallback/clientMiddleware) into its own module and import it from both sides","Alternatively, disable `splitRouteModules` in react-router.config.ts if the refactor is not worth it"],"exampleFix":"// before (app/routes/todos.tsx) — shared helper blocks splitting\nasync function fetchTodos() { /* ... */ }\nexport async function clientLoader() { return { todos: await fetchTodos() }; }\nexport default function Todos({ loaderData }) { /* uses loaderData only */ }\n\n// after — app/lib/todos.ts\nexport async function fetchTodos() { /* ... */ }\n// app/routes/todos.tsx\nimport { fetchTodos } from '~/lib/todos';\nexport async function clientLoader() { return { todos: await fetchTodos() }; }\nexport default function Todos() { /* ... */ }","handlingStrategy":"validation","validationCode":"// Before enabling splitRouteModules, ensure shared module-scope bindings\n// are not referenced by both default and split exports (lint rule sketch)\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst sharesModuleScopeCode = (src: string): boolean =>\n  /(?:^|\\n)(?:async\\s+)?(?:function|const|let|class)\\s+\\w+/.test(src) &&\n  /export\\s+(?:async\\s+)?(?:function|const)\\s+(?:clientLoader|clientAction|clientMiddleware|HydrateFallback)/.test(src);","typeGuard":null,"tryCatchPattern":"try {\n  await build();\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Error splitting route module')) {\n    // message names the file + exports — extract shared helpers into ~/lib and rebuild\n  }\n}","preventionTips":["Keep route files thin: shared helpers live in `app/lib/*`, routes only import them","Enable `splitRouteModules` on a clean branch and let CI validate all routes","Avoid module-scope mutable state inside route files"],"tags":["code-splitting","route-modules","build","optimization"],"backgroundTag":"route-module-chunking-failed","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}