remix-run/react-router · error
${reactRouterConfigFile} must provide a default export
Error message
${reactRouterConfigFile} must provide a default export What it means
The React Router config file was imported through the Vite runner, but its module has no `default` export (`configModule.default === undefined`). React Router requires the config file to export its configuration object as the default export, so config resolution returns this error before any validation runs.
Source
Thrown at packages/react-router-dev/config/config.ts:467
viteRunnerContext: ViteRunner.Context;
reactRouterConfigFile?: string;
skipRoutes?: boolean;
validateConfig?: ValidateConfigFunction;
}): 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}`);
}
}View on GitHub (pinned to 6beaca3952)
Solutions
- Add `export default` to the config object in the file named in the error.
- If using a named export locally, add `export default config;` at the end of the file.
- Verify there is exactly one default export and it is the top-level config object.
- Restart the dev server after fixing (config is loaded once per readConfig call).
Example fix
// before: react-router.config.ts
export const config = {
ssr: true,
};
// after
export default {
ssr: true,
}; Defensive patterns
Strategy: type-guard
Validate before calling
// quick check before starting the toolchain
import fs from "node:fs";
const src = fs.readFileSync("react-router.config.ts", "utf8");
if (!/^export\s+default\b/m.test(src) && !/^export\s*\{[^}]*\bas\s+default\b/m.test(src)) {
throw new Error("react-router.config.ts must have a default export");
} Type guard
function hasDefaultConfig(mod: unknown): mod is { default: object } {
return (
typeof mod === "object" &&
mod !== null &&
"default" in mod &&
typeof (mod as { default: unknown }).default === "object"
);
} Prevention
- Always author the config as `export default { ... }`.
- If splitting config into helpers, still re-export the object as default at the end.
- Codemod reviews of the config file should assert the default export survives.
When it happens
Trigger: Writing `react-router.config.ts` with only named exports (e.g. `export const config = {...}`), exporting nothing, or exporting only types/side effects; the file loads fine but `mod.default` is undefined.
Common situations: Converting a shared config snippet into named exports and forgetting the `default` keyword; copy-pasting an example that uses `export default` into a file where it gets wrapped or re-exported without default; refactoring tools (codemods) stripping the default export.
Related errors
- ${reactRouterConfigFile} must export a config
- 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/65dc2d20b91804e7.
Report an issue: GitHub.