{"record":{"id":"29ccba274cce8b54","repo":"remix-run/react-router","slug":"the-react-router-vite-plugin-requires-the-use-of-a","errorCode":null,"errorMessage":"The React Router Vite plugin requires the use of a Vite config file","messagePattern":"The React Router Vite plugin requires the use of a Vite config file","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/plugin.ts","lineNumber":1429,"sourceCode":"          };\n        }\n      },\n      async configResolved(resolvedViteConfig) {\n        await initEsModuleLexer;\n\n        viteConfig = resolvedViteConfig;\n        invariant(viteConfig);\n\n        // We load the same Vite config file again for the child compiler so\n        // that both parent and child compiler's plugins have independent state.\n        // If we re-used the `viteUserConfig.plugins` array for the child\n        // compiler, it could lead to mutating shared state between plugin\n        // instances in unexpected ways, e.g. during `vite build` the\n        // `configResolved` plugin hook would be called with `command = \"build\"`\n        // by parent and then `command = \"serve\"` by child, which some plugins\n        // may respond to by updating state referenced by the parent.\n        if (!viteConfig.configFile) {\n          throw new Error(\n            \"The React Router Vite plugin requires the use of a Vite config file\",\n          );\n        }\n\n        let vite = getVite();\n\n        let childCompilerConfigFile = await vite.loadConfigFromFile(\n          {\n            command: viteConfig.command,\n            mode: viteConfig.mode,\n          },\n          viteConfig.configFile,\n        );\n\n        invariant(\n          childCompilerConfigFile,\n          \"Vite config file was unable to be resolved for React Router child compiler\",\n        );","sourceCodeStart":1411,"sourceCodeEnd":1447,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/vite/plugin.ts#L1411-L1447","documentation":"For Framework Mode builds, the React Router plugin spawns a child compiler (for SSR/client variants) and re-loads the same Vite config file so parent and child plugin instances keep independent state. If `viteConfig.configFile` is falsy, there is no file to reload, so the plugin throws. An inline or disabled config file cannot be duplicated safely and would risk shared mutable plugin state between compilers.","triggerScenarios":"Running a build with `--configFile false` (or `configFile: false` passed programmatically); calling `vite.build({ configFile: false, plugins: [reactRouter()] })` with an entirely inline config; setting `configFile` to a path Vite fails to resolve so the resolved config's `configFile` ends up null.","commonSituations":"Programmatic builds in monorepo tooling that construct Vite configs inline; scripts copied from plain Vite projects that disable config discovery; moving the config to a non-default filename without pointing `--config` at it.","solutions":["Create a real vite.config.ts (or .js/.mjs) at the project root containing the `reactRouter()` plugin and build without `--configFile false`","If using a non-default filename, pass it explicitly: `react-router build --config vite.config.rr.ts` so `configFile` resolves to a value","For programmatic builds, write the inline config to a temporary file and point `configFile` at it rather than disabling config files"],"exampleFix":"# before\nvite build --configFile false\n\n# after\nreact-router build  # vite.config.ts with reactRouter() plugin is auto-discovered","handlingStrategy":"validation","validationCode":"// Before invoking programmatic builds, confirm a config file exists\nimport fs from 'node:fs';\nif (!['vite.config.ts', 'vite.config.js', 'vite.config.mts', 'vite.config.mjs'].some((f) => fs.existsSync(path.join(root, f)))) {\n  throw new Error('A vite.config.* file is required for React Router builds');\n}","typeGuard":"const configFilePathExists = (root: string): boolean =>\n  ['ts', 'mts', 'js', 'mjs'].some((ext) =>\n    fs.existsSync(path.join(root, `vite.config.${ext}`)),\n  );","tryCatchPattern":"try {\n  await build({ configFile: false });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('requires the use of a Vite config file')) {\n    // write inline config to a temp file and rebuild with configFile set to it\n  } else throw e;\n}","preventionTips":["Always keep a real vite.config.ts in React Router Framework Mode projects","Never pass `--configFile false` to react-router/vite builds","For programmatic configs, write them to a file and reference via `configFile`"],"tags":["vite","config","build","child-compiler"],"backgroundTag":"missing-config-file","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}