remix-run/react-router · error

The "@vitejs/plugin-rsc" plugin should be placed after the…

Error message

The "@vitejs/plugin-rsc" plugin should be placed after the React Router RSC plugin in your Vite config

What it means

In RSC Framework Mode (`unstable_reactRouterRSC`), plugin order matters: the React Router RSC plugin (`react-router/rsc`) must come before `@vitejs/plugin-rsc` (`rsc`) in the plugins array so RR can register its virtual modules and transforms ahead of the RSC compiler. A `validate-plugin-order` plugin compares plugin indices in `configResolved` and throws when `@vitejs/plugin-rsc` is placed first.

Solutions

  1. Reorder vite.config.ts so `reactRouter()` comes first and `@vitejs/plugin-rsc()` after it
  2. Use the RSC template (`rsc-vite-framework`) as the reference ordering
  3. Also verify `@mdx-js/rollup` sits before the React Router plugin if present (checked in the same validator)

Example fix

// before (vite.config.ts)
plugins: [rsc(), reactRouter()],

// after
import { reactRouter } from '@react-router/dev/vite';
import { rsc } from '@vitejs/plugin-rsc';
plugins: [reactRouter(), rsc()],
Defensive patterns

Strategy: validation

Validate before calling

// Assert plugin order before exporting the config
const plugins = [mdx(), reactRouter(), rsc()];
const idx = (name: string) => plugins.findIndex((p: any) => p.name?.includes(name));
if (idx('rsc') >= 0 && idx('rsc') < idx('react-router')) {
  throw new Error('@vitejs/plugin-rsc must come after reactRouter()');
}
if (idx('@mdx-js/rollup') >= 0 && idx('@mdx-js/rollup') > idx('react-router')) {
  throw new Error('@mdx-js/rollup must come before reactRouter()');
}
export default defineConfig({ plugins });

Try / catch

try {
  await build();
} catch (e) {
  if (e instanceof Error && e.message.includes('should be placed after')) {
    // reorder plugins per the message and rebuild
  }
}

Prevention

When it happens

Trigger: Adding `rsdc()`/`@vitejs/plugin-rsc()` to `plugins: [...]` before `reactRouter({ unstable_reactRouterRSC: ... })`; auto-generated configs or templates inserting the RSC plugin at the top; spreading user plugins before the RR plugin in a shared config.

Common situations: Hand-editing vite.config.ts while trying RSC mode; copying plugin arrays from docs where order was different; merging configs where `...restPlugins` lands ahead of `reactRouter()`.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/7ccd9b0d89a230b0. Report an issue: GitHub.

Appendix: source

Thrown at packages/react-router-dev/vite/plugins/validate-plugin-order.ts:21

export function validatePluginOrder(): Vite.Plugin {
  return {
    name: "react-router:validate-plugin-order",
    configResolved(viteConfig) {
      let pluginIndex = (pluginName: string | string[]) => {
        pluginName = Array.isArray(pluginName) ? pluginName : [pluginName];
        return viteConfig.plugins.findIndex((plugin) =>
          pluginName.includes(plugin.name),
        );
      };

      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 6beaca3952)