remix-run/react-router · error

${reactRouterConfigFile} must export a config

Error message

${reactRouterConfigFile} must export a config

What it means

The config file has a default export, but `typeof configModule.default !== "object"` — it is a string, number, boolean, function, or similar non-object value. React Router expects the default export to be a plain config object whose keys are option names (`ssr`, `prerender`, `buildDirectory`, ...), so any primitive (or function) default export is rejected before further processing.

Source

Thrown at packages/react-router-dev/config/config.ts:471

}): Promise<ConfigResult> {
  let reactRouterUserConfig: ReactRouterConfig = {};

  if (reactRouterConfigFile) {
    try {
      if (!fs.existsSync(reactRouterConfigFile)) {
        return err(`${reactRouterConfigFile} no longer exists`);
      }

      let configModule = await viteRunnerContext.runner.import(
        reactRouterConfigFile,
      );

      if (configModule.default === undefined) {
        return err(`${reactRouterConfigFile} must provide a default export`);
      }

      if (typeof configModule.default !== "object") {
        return err(`${reactRouterConfigFile} must export a config`);
      }

      reactRouterUserConfig = configModule.default;

      if (validateConfig) {
        const error = validateConfig(reactRouterUserConfig);
        if (error) {
          return err(error);
        }
      }
    } catch (error) {
      return err(`Error loading ${reactRouterConfigFile}: ${error}`);
    }
  }

  // Prevent mutations to the user config
  reactRouterUserConfig = deepFreeze(cloneDeep(reactRouterUserConfig));

View on GitHub (pinned to 6beaca3952)

Solutions

  1. Change the default export to a plain object literal containing the config options.
  2. If you wanted a function-style config, compute the object first and export the result.
  3. Check for accidental re-export of an option value instead of the config object.
  4. Re-run the build/dev command to confirm the error clears.

Example fix

// before: react-router.config.ts
export default () => ({ ssr: true }); // function, not object

// after
export default {
  ssr: true,
};
Defensive patterns

Strategy: type-guard

Validate before calling

import cfg from "./react-router.config";
if (typeof cfg !== "object" || cfg === null || Array.isArray(cfg) === undefined) {
  // mirror of the library check
}
if (typeof cfg !== "object" || cfg === null) {
  throw new Error("react-router config default export must be a plain object");
}

Type guard

function isPlainObjectConfig(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}

Prevention

When it happens

Trigger: Default-exporting a primitive or function from `react-router.config.ts`, e.g. `export default "ssr"`, `export default 42`, or `export default () => ({ ssr: true })` (a function is `typeof "function"`, not `"object"`).

Common situations: Attempting a 'lazy config' pattern (`export default () => config`) which React Router does not support; typos like `export default ssr;` where `ssr` is a boolean variable; converting a JSON config import into a JS value of the wrong shape.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/639d4dbeef1b95ad. Report an issue: GitHub.