remix-run/react-router · error · Error

Error splitting route module: ${normalizeRelativeFilePath(id

Error message

Error splitting route module: ${normalizeRelativeFilePath(id, ctx.reactRouterConfig)}\n\n${invalidChunks.map((name) => `- ${name}`).join("\n")}\n\n${plural ? "These exports" : "This export"} could not be split into ${plural ? "their own chunks" : "its own chunk"} because ${plural ? "they share" : "it shares"} code with other exports. You should extract any shared code into its own module and then import it within the route module.

What it means

Thrown by `validateRouteChunks` when one or more named route chunks (`clientAction`, `clientLoader`, `HydrateFallback`, `ErrorBoundary`) cannot be safely isolated. React Router splits these exports into separate chunks so the client doesn't download, e.g., the action code on first paint. If an export shares code (variables, helper functions, imports) with another export, the splitter would have to duplicate or pull in shared code, so it refuses and lists the offending chunk names.

Source

Thrown at packages/react-router-dev/vite/plugin.ts:3356

  ctx: ReactRouterPluginContext;
  id: string;
  valid: Record<Exclude<RouteChunkName, "main">, boolean>;
}): void {
  if (isRootRouteModuleId(ctx, id)) {
    return;
  }

  let invalidChunks = Object.entries(valid)
    .filter(([_, isValid]) => !isValid)
    .map(([chunkName]) => chunkName);

  if (invalidChunks.length === 0) {
    return;
  }

  let plural = invalidChunks.length > 1;

  throw new Error(
    [
      `Error splitting route module: ${normalizeRelativeFilePath(
        id,
        ctx.reactRouterConfig,
      )}`,

      invalidChunks.map((name) => `- ${name}`).join("\n"),

      `${plural ? "These exports" : "This export"} could not be split into ${
        plural ? "their own chunks" : "its own chunk"
      } because ${
        plural ? "they share" : "it shares"
      } code with other exports. You should extract any shared code into its own module and then import it within the route module.`,
    ].join("\n\n"),
  );
}

export async function cleanBuildDirectory(

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Extract the shared code into its own module (a sibling `.ts` file) and import it from each export.
  2. Inline the shared logic per export if extraction is not desirable.
  3. Reduce cross-export coupling by passing data through arguments instead of module-scope variables.
  4. Rebuild — the listed chunk names tell you exactly which exports to refactor.

Example fix

// before: clientAction and clientLoader share a helper
const fetcher = (id) => api.get(`/x/${id}`);
export const clientAction = () => fetcher(1);
export const clientLoader = () => fetcher(2);
// after: extract shared helper
// app/routes/_shared.ts
export const fetcher = (id) => api.get(`/x/${id}`);
// app/routes/foo.ts
import { fetcher } from "./_shared";
export const clientAction = () => fetcher(1);
export const clientLoader = () => fetcher(2);
Defensive patterns

Strategy: validation

Validate before calling

// Statically detect shared top-level identifiers across chunks
const findSharedBindings = (exports: string[], moduleScope: string[]) => {
  return moduleScope.filter(name =>
    exports.some(e => references(name, e)) &&
    exports.some(e => e !== references && references(name, e)));
};

Prevention

When it happens

Trigger: A route module declares `clientAction` and `clientLoader` that both reference a shared in-file helper or a shared top-level `const`; an `ErrorBoundary` that imports a large shared module also used by the route's `default`; route chunks that close over module-scope mutable state.

Common situations: Co-locating action/loader logic in one route file with shared helpers; refactoring that pulls shared utilities up to module scope; large route modules with intertwined exports.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/c35f3f962d49791c. Report an issue: GitHub.