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
- Change the default export to a plain object literal containing the config options.
- If you wanted a function-style config, compute the object first and export the result.
- Check for accidental re-export of an option value instead of the config object.
- 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
- Export an object literal, never a function or primitive.
- If you compute config conditionally, compute first then `export default result`.
- Add `satisfies ReactRouterConfig` (type-only) to catch shape mistakes in your editor.
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
- ${reactRouterConfigFile} must provide a default export
- React Router Vite plugin not found in Vite config
- Custom Vite manifest paths are not supported
- The React Router Vite plugin requires the use of a Vite conf
- The "serverBundles" function must return a string
AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18).
Data as JSON: /api/errors/639d4dbeef1b95ad.
Report an issue: GitHub.