# How React Router Integrates with Framework Tooling: The Complete Guide to @react-router/dev

> Discover how @react-router/dev integrates React Router with Vite for zero-config SSR, auto code-splitting, HMR, and build manifests. Enhance your React development workflow.

- Repository: [Remix/react-router](https://github.com/remix-run/react-router)
- Tags: deep-dive
- Published: 2026-03-06

---

**`@react-router/dev` is the framework-mode integration layer that plugs React Router into Vite, enabling zero-config SSR, automatic code-splitting, HMR, and build-time manifest generation.**

React Router's evolution into a framework-ready library relies on `@react-router/dev`, the official integration package that bridges the router with modern bundlers. This tooling layer converts a standard React Router application into a full-stack framework with server-side rendering, type-safe routes, and optimized production builds. Understanding how React Router framework tooling works is essential for developers building production-grade applications with the latest React Router architecture.

## What Is @react-router/dev?

`@react-router/dev` exports a Vite plugin (`reactRouterVitePlugin`) that transforms a React Router application into a full-stack framework. Unlike traditional React Router usage where you manually set up a bundler and server, this package handles the entire build lifecycle, from development server configuration to production bundle optimization.

The plugin operates as a bridge between React Router's data router API and Vite's build system, injecting virtual modules, managing route manifests, and coordinating server-side rendering without requiring manual configuration.

## How the Vite Plugin Bridges React Router and Bundlers

The integration works through eight distinct phases, each handled by specific modules within `packages/react-router-dev`.

### Configuration Loading and Route Resolution

The plugin first detects and loads the React Router configuration using `createConfigLoader` in [`config/config.ts`](https://github.com/remix-run/react-router/blob/main/config/config.ts). This reads [`react-router.config.ts`](https://github.com/remix-run/react-router/blob/main/react-router.config.ts) (or defaults to [`routes.ts`](https://github.com/remix-run/react-router/blob/main/routes.ts) in `app/`) and resolves all route files into a manifest structure that the build system can consume.

### Virtual Module Generation

In [`vite/virtual-module.ts`](https://github.com/remix-run/react-router/blob/main/vite/virtual-module.ts), the plugin creates a virtual Vite module graph. These virtual modules—including `server-build`, `browser-manifest`, and `hmr-runtime`—are injected into the Vite build but do not exist as physical files on disk. This allows the plugin to dynamically expose route manifests and build metadata to both client and server code.

### Build-Time Manifest Generation

After Vite emits its own manifest, `generateReactRouterManifestsForBuild` (located in [`vite/plugin.ts`](https://github.com/remix-run/react-router/blob/main/vite/plugin.ts) around lines 569-627) walks the manifest to extract CSS and route chunk assets. It writes a **browser manifest** (exposed as `window.__reactRouterManifest`) and a **server manifest** used by the SSR entry to map URLs to route modules.

### Route Module Code Splitting

The `detectRouteChunksIfEnabled` function in [`vite/route-chunks.ts`](https://github.com/remix-run/react-router/blob/main/vite/route-chunks.ts) identifies "route chunks"—specifically `clientAction`, `clientLoader`, `clientMiddleware`, and `HydrateFallback` exports. The plugin rewrites imports so these become separate lazy-loaded bundles, reducing the initial JavaScript payload while maintaining framework behavior.

### Hot Module Replacement (HMR)

During development, the plugin sets up a Vite dev server child compiler that watches route source files. Using `es-module-lexer`, it re-parses exports and triggers Vite’s HMR runtime to replace route modules without full page reloads. This logic is handled in [`vite/plugin.ts`](https://github.com/remix-run/react-router/blob/main/vite/plugin.ts) around line 900.

### Server-Side Bundle Generation

The plugin generates a **server entry** that imports every route module (or a stub when SPA mode is enabled) and exports the router configuration (`routes`, `entry`, `prerender`, etc.). This server bundle is built in a separate Vite environment (`ssr`), optionally using the new Vite *Environment API* when the `v8_viteEnvironmentApi` future flag is enabled. See `getServerEntry` in [`vite/plugin.ts`](https://github.com/remix-run/react-router/blob/main/vite/plugin.ts) (lines 892-925).

### Type Generation

The built-in `typegen` watcher runs during `vite serve` to produce [`react-router-dev.d.ts`](https://github.com/remix-run/react-router/blob/main/react-router-dev.d.ts) that mirrors route exports (`loader`, `action`, `params`, etc.). This provides full TypeScript intellisense for route modules without manual type definitions. The implementation resides in [`typegen/index.ts`](https://github.com/remix-run/react-router/blob/main/typegen/index.ts).

## Configuration Examples

### Minimal Vite Configuration

To enable React Router framework tooling, add the plugin to your Vite configuration:

```javascript
// vite.config.js
import { defineConfig } from 'vite';
import { reactRouterVitePlugin } from '@react-router/dev/vite/plugin';

export default defineConfig({
  plugins: [reactRouterVitePlugin()],
});

```

Behind the scenes, Vite calls `reactRouterVitePlugin()`, which registers hooks (`config`, `configResolved`, `configureServer`, `closeBundle`) that implement the integration steps detailed above.

### Typical Project Layout

Framework mode expects a specific directory structure:

```

my-app/
├─ app/
│  ├─ routes/
│  │  ├─ _index.tsx          // → “/”
│  │  ├─ about.tsx           // → “/about”
│  │  └─ blog.$slug.tsx      // → “/blog/:slug”
│  └─ routes.ts              // (generated by flatRoutes)
├─ vite.config.js
└─ package.json

```

The [`routes.ts`](https://github.com/remix-run/react-router/blob/main/routes.ts) file is usually created with **`flatRoutes()`** from `@react-router/fs-routes`. The dev plugin reads this file, builds the route manifest, and injects the virtual module IDs required for SSR.

### Custom Server Entry (Advanced)

For custom server implementations, you can access the development manifest directly:

```typescript
// server/entry.server.ts
import { createRequestHandler } from 'react-router';
import { getReactRouterManifestForDev } from '@react-router/dev/vite/plugin';

export const requestHandler = async (req: Request) => {
  const manifest = await getReactRouterManifestForDev();
  return createRequestHandler({ request: req, manifest });
};

```

During production builds, the plugin writes a static manifest file ([`manifest-XXXXX.js`](https://github.com/remix-run/react-router/blob/main/manifest-XXXXX.js)) that the server imports directly, eliminating the need for runtime round-trips.

### Experimental Environment API

To enable multiple SSR environments (e.g., separate bundles for Node.js and Cloudflare):

```javascript
// vite.config.js
import { defineConfig } from 'vite';
import { reactRouterVitePlugin } from '@react-router/dev/vite/plugin';

export default defineConfig({
  plugins: [reactRouterVitePlugin()],
  reactRouter: {
    future: {
      v8_viteEnvironmentApi: true,
    },
  },
});

```

This activates the Vite *Environment API*, allowing you to define multiple environments in [`reactRouter.config.ts`](https://github.com/remix-run/react-router/blob/main/reactRouter.config.ts) that build in parallel with isolated server bundles and manifests.

## Key Source Files in @react-router/dev

| File | Role | Link |
|------|------|------|
| [`vite/plugin.ts`](https://github.com/remix-run/react-router/blob/main/vite/plugin.ts) | Core Vite plugin implementation (`reactRouterVitePlugin`) | [View](https://github.com/remix-run/react-router/blob/main/packages/react-router-dev/vite/plugin.ts) |
| [`vite/virtual-module.ts`](https://github.com/remix-run/react-router/blob/main/vite/virtual-module.ts) | Creates virtual modules exposing manifest and HMR runtime | [View](https://github.com/remix-run/react-router/blob/main/packages/react-router-dev/vite/virtual-module.ts) |
| [`vite/route-chunks.ts`](https://github.com/remix-run/react-router/blob/main/vite/route-chunks.ts) | Detects and builds separate route-chunk bundles | [View](https://github.com/remix-run/react-router/blob/main/packages/react-router-dev/vite/route-chunks.ts) |
| [`config/config.ts`](https://github.com/remix-run/react-router/blob/main/config/config.ts) | Loads and validates React Router configuration | [View](https://github.com/remix-run/react-router/blob/main/packages/react-router-dev/config/config.ts) |
| [`typegen/index.ts`](https://github.com/remix-run/react-router/blob/main/typegen/index.ts) | Generates TypeScript types for route exports | [View](https://github.com/remix-run/react-router/tree/main/packages/react-router-dev/typegen) |
| [`vite/plugins/validate-plugin-order.ts`](https://github.com/remix-run/react-router/blob/main/vite/plugins/validate-plugin-order.ts) | Ensures correct plugin ordering in Vite config | [View](https://github.com/remix-run/react-router/blob/main/packages/react-router-dev/vite/plugins/validate-plugin-order.ts) |
| [`vite/plugins/prerender.ts`](https://github.com/remix-run/react-router/blob/main/vite/plugins/prerender.ts) | Implements static site generation via `prerender` hook | [View](https://github.com/remix-run/react-router/blob/main/packages/react-router-dev/vite/plugins/prerender.ts) |

These files collectively enable `@react-router/dev` to bridge React Router’s data-router API with Vite’s build lifecycle, delivering a seamless full-stack development experience.

## Summary

- **`@react-router/dev`** provides the `reactRouterVitePlugin` that transforms React Router into a full-stack framework via Vite integration.
- The plugin handles **configuration loading** ([`config/config.ts`](https://github.com/remix-run/react-router/blob/main/config/config.ts)), **virtual module generation** ([`vite/virtual-module.ts`](https://github.com/remix-run/react-router/blob/main/vite/virtual-module.ts)), and **build-time manifest creation** ([`vite/plugin.ts`](https://github.com/remix-run/react-router/blob/main/vite/plugin.ts)).
- **Route-level code splitting** is automatic for `clientAction`, `clientLoader`, and `HydrateFallback` exports via [`vite/route-chunks.ts`](https://github.com/remix-run/react-router/blob/main/vite/route-chunks.ts).
- **Hot Module Replacement** works at the route level without full page reloads, and **type generation** provides full TypeScript intellisense for route exports.
- The plugin supports **multiple SSR environments** through the experimental Vite Environment API (`v8_viteEnvironmentApi`).

## Frequently Asked Questions

### How do I add React Router framework tooling to an existing Vite project?

Install `@react-router/dev` and add the plugin to your [`vite.config.js`](https://github.com/remix-run/react-router/blob/main/vite.config.js). Import `reactRouterVitePlugin` from `@react-router/dev/vite/plugin` and include it in the plugins array. Ensure your project has the standard `app/routes` directory structure or configure custom paths in [`react-router.config.ts`](https://github.com/remix-run/react-router/blob/main/react-router.config.ts).

### What is the difference between React Router library mode and framework mode?

Library mode requires you to manually configure your bundler and server, using `createBrowserRouter` or similar APIs directly. Framework mode, enabled by `@react-router/dev`, provides zero-config SSR, automatic code-splitting, type-safe routes, and built-in HMR by integrating deeply with Vite's build lifecycle.

### How does route-level code splitting work in @react-router/dev?

The plugin analyzes route modules using `detectRouteChunksIfEnabled` in [`vite/route-chunks.ts`](https://github.com/remix-run/react-router/blob/main/vite/route-chunks.ts) to identify exports like `clientAction`, `clientLoader`, `clientMiddleware`, and `HydrateFallback`. It then rewrites imports so these exports become separate lazy-loaded bundles, reducing initial JavaScript payload while maintaining framework behavior.

### Can I use @react-router/dev with custom server environments like Cloudflare Workers?

Yes. Enable the experimental `v8_viteEnvironmentApi` future flag in your Vite config to use the Vite Environment API. This allows you to define multiple SSR environments (e.g., Node.js, Cloudflare, Deno) in [`reactRouter.config.ts`](https://github.com/remix-run/react-router/blob/main/reactRouter.config.ts), each generating isolated server bundles and manifests during the build process.