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

  1. In vite.config.ts, put mdx() BEFORE reactRouter(): `plugins: [mdx(), reactRouter()]`.
  2. Re-run dev/build to confirm configResolved no longer throws.
  3. If a framework preset injects MDX, ensure the preset runs before React Router or configure the preset order accordingly.
  4. 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

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


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/33b46107e0ec2003. Report an issue: GitHub.