remix-run/react-router · error · Error
The "@mdx-js/rollup" plugin should be placed before the Reac
Error message
The "@mdx-js/rollup" plugin should be placed before the React Router plugin in your Vite config
What it means
Thrown from configResolved of react-router:validate-plugin-order when `@mdx-js/rollup` is present and resolves to a higher index than the React Router plugin (`react-router` or `react-router/rsc`). MDX must transform .mdx files before React Router consumes the module graph, so it must precede React Router in the plugins array.
Source
Thrown at packages/react-router-dev/vite/plugins/validate-plugin-order.ts:32
let reactRouterRscPluginIndex = pluginIndex("react-router/rsc");
let viteRscPluginIndex = pluginIndex("rsc");
if (
reactRouterRscPluginIndex >= 0 &&
viteRscPluginIndex >= 0 &&
reactRouterRscPluginIndex > viteRscPluginIndex
) {
throw new Error(
`The "@vitejs/plugin-rsc" plugin should be placed after the React Router RSC plugin in your Vite config`,
);
}
let reactRouterPluginIndex = pluginIndex([
"react-router",
"react-router/rsc",
]);
let mdxPluginIndex = pluginIndex("@mdx-js/rollup");
if (mdxPluginIndex >= 0 && mdxPluginIndex > reactRouterPluginIndex) {
throw new Error(
`The "@mdx-js/rollup" plugin should be placed before the React Router plugin in your Vite config`,
);
}
},
};
}
View on GitHub (pinned to 1fd704a7da)
Solutions
- In vite.config.ts, put mdx() BEFORE reactRouter(): `plugins: [mdx(), reactRouter()]`.
- Re-run dev/build to confirm configResolved no longer throws.
- If a framework preset injects MDX, ensure the preset runs before React Router or configure the preset order accordingly.
- Update @mdx-js/rollup to a version whose plugin.name is still recognized (`@mdx-js/rollup`).
Example fix
// vite.config.ts
// before
import mdx from '@mdx-js/rollup';
import { reactRouter } from '@react-router/dev/vite';
export default defineConfig({ plugins: [reactRouter(), mdx()] });
// after
export default defineConfig({ plugins: [mdx(), reactRouter()] }); Defensive patterns
Strategy: validation
Validate before calling
import mdx from '@mdx-js/rollup';
import { reactRouter } from '@react-router/dev/vite';
const plugins = [mdx(), reactRouter()]; // assert mdx index < reactRouter index
const mdxIdx = plugins.findIndex((p) => p.name.includes('@mdx-js/rollup'));
const rrIdx = plugins.findIndex((p) => p.name.includes('react-router'));
if (mdxIdx >= 0 && rrIdx >= 0 && mdxIdx > rrIdx) throw new Error('mdx() must come before reactRouter()'); Type guard
function mdxBeforeReactRouter(plugins: { name: string }[]): boolean {
const mdx = plugins.findIndex((p) => p.name.includes('@mdx-js/rollup'));
const rr = plugins.findIndex((p) => p.name.includes('react-router'));
return !(mdx >= 0 && rr >= 0 && mdx > rr);
} Prevention
- Always register mdx() above reactRouter() in vite.config.ts.
- If a preset injects MDX, verify it runs before React Router.
- Add a comment in the config documenting the required order.
When it happens
Trigger: vite.config.ts lists `mdx()` after `reactRouter()` in the plugins array, so mdxPluginIndex > reactRouterPluginIndex. The check uses findIndex over resolved plugin names containing `@mdx-js/rollup`.
Common situations: Adding MDX to an existing React Router app by appending mdx() at the end of plugins. Following docs that show `plugins: [reactRouter(), mdx()]` (wrong order). Using a starter that registered MDX lazily.
Related errors
- The "@vitejs/plugin-rsc" plugin should be placed after the R
- React Router Vite plugin not found in Vite config
- Custom Vite manifest paths are not supported
- Could not resolve module ID for ${url}
- The React Router Vite plugin requires the use of a Vite conf
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/33b46107e0ec2003.
Report an issue: GitHub.