Set up React Router framework mode with Vite and Nitro. This setup uses Nitro to run the React Router server build in development and production.
- Add the React Router and Nitro plugins to your Vite config
- Configure Nitro to emit its server alongside React Router's client build
- Create an SSR handler using React Router's request handler
- Add Nitro server routes
- Define routes using React Router's route configuration
Add the React Router, Tailwind CSS, and Nitro plugins to your Vite config:
import { reactRouter } from "@react-router/dev/vite";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";
import reactRouterConfig from "./react-router.config";
export default defineConfig({
plugins: [
tailwindcss(),
reactRouter(),
nitro({
serverDir: "./server",
output: {
dir: reactRouterConfig.buildDirectory,
serverDir: `${reactRouterConfig.buildDirectory}/server`,
publicDir: `${reactRouterConfig.buildDirectory}/client`,
},
}),
],
resolve: {
tsconfigPaths: true,
},
environments: {
ssr: { build: { rollupOptions: { input: "./server/ssr.ts" } } },
},
});React Router creates the ssr environment and builds browser assets into build/client. The custom input points to the fetch-compatible handler in server/ssr.ts. Nitro scans server/ for its own routes, then emits the production server into build/server.
Enable SSR and keep the build directory in sync with the Nitro output configuration:
import type { Config } from "@react-router/dev/config";
export default {
ssr: true,
buildDirectory: "build",
} satisfies Config;React Router generates the server build exposed by virtual:react-router/server-build and the client assets required for hydration.
Create an SSR handler that delegates incoming requests to React Router:
import { createRequestHandler } from "react-router";
export default {
fetch: createRequestHandler(
() => import("virtual:react-router/server-build"),
import.meta.env.MODE
),
};Nitro invokes the exported Web fetch handler through the Vite ssr service in development and production. The request handler loads React Router's generated server build and renders the matched route.
Add API and other server routes under server/routes/:
import { defineHandler } from "nitro";
export default defineHandler(() => {
return { status: "OK" };
});This route is available at /health and is handled by Nitro before requests fall through to the React Router SSR service.
Declare the application's route modules in app/routes.ts:
import { type RouteConfig, index } from "@react-router/dev/routes";
export default [index("routes/home.tsx")] satisfies RouteConfig;The index route renders the home page at /.