Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ Choose the guide that matches your React Router major version:

## What the bridge does

The setup adds a small `server/ssr.ts` file. It turns React Router's generated server build into a standard Fetch API handler that Nitro can run. The Vite config then points Nitro's server and public output at the same `build` directory React Router uses.
The setup adds a small `server/ssr.ts` file. It turns React Router's generated server build into a standard Fetch API handler that Nitro can run. A small Vite plugin keeps React Router's client manifest where React Router expects it, while Nitro produces the output required by the selected deployment preset, such as `.output` for a local Node.js server or `.vercel/output` on Vercel.

This is configuration in your application, not a separate React Router adapter. Your React Router routes remain React Router routes, while Nitro owns the HTTP server and Workflow SDK uses Nitro's lifecycle and routing.
34 changes: 18 additions & 16 deletions docs/content/docs/v5/getting-started/react-router/v7.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ This integration requires Nitro v3.

## Enable the Vite Environment API

React Router v7 keeps the Vite Environment API behind a future flag. Enable the required flag and set an explicit build directory:
React Router v7 keeps the Vite Environment API behind a future flag. Enable the required flag:

```typescript title="react-router.config.ts" lineNumbers
import type { Config } from "@react-router/dev/config";

export default {
ssr: true,
buildDirectory: "build", // [!code highlight]
buildDirectory: "build",
future: {
v8_viteEnvironmentApi: true, // [!code highlight]
},
Expand Down Expand Up @@ -108,21 +108,27 @@ Update `vite.config.ts`:
```typescript title="vite.config.ts" lineNumbers
import { reactRouter } from "@react-router/dev/vite";
import { nitro } from "nitro/vite";
import { cp } from "node:fs/promises";
import { resolve } from "node:path";
import { defineConfig } from "vite";
import { workflow } from "workflow/vite";
import reactRouterConfig from "./react-router.config";

export default defineConfig({
plugins: [
reactRouter(),
nitro({
serverDir: "./server",
output: {
dir: reactRouterConfig.buildDirectory,
serverDir: `${reactRouterConfig.buildDirectory}/server`,
publicDir: `${reactRouterConfig.buildDirectory}/client`,
nitro({ serverDir: "./server" }),
{
name: "react-router-nitro-manifest",
applyToEnvironment: (environment) => environment.name === "client",
async writeBundle(options) {
await cp(
resolve(options.dir!, ".vite"),
resolve(reactRouterConfig.buildDirectory, "client/.vite"),
{ recursive: true },
);
},
}),
},
workflow({ dirs: ["workflows"] }),
],
environments: {
Expand All @@ -137,7 +143,7 @@ export default defineConfig({
});
```

Keep `dirs: ["workflows"]` so subsequent builds do not scan generated files under `build`. Place `reactRouter()` before `nitro()` in the plugin array.
The small `react-router-nitro-manifest` plugin copies React Router's Vite manifest back to `build/client` after Nitro writes the client build to its deployment output. Keep `dirs: ["workflows"]` so Workflow SDK only scans your source workflow directory. Do not set Nitro's `output.dir`; Nitro uses it to produce the output expected by each deployment preset.

</Step>

Expand Down Expand Up @@ -209,7 +215,7 @@ Build and start the production server:

```bash
pnpm vite build
node ./build/server/index.mjs
node ./.output/server/index.mjs
```

You can inspect local runs with `pnpm workflow web`.
Expand All @@ -228,10 +234,6 @@ Set `future.v8_viteEnvironmentApi` to `true` in `react-router.config.ts`.

Check that the `ssr` environment input points to `./server/ssr.ts`.

### A second build tries to compile files under `build/server`

Use `workflow({ dirs: ["workflows"] })`, remove the existing `build` directory once, and rebuild.

### `vite build` finishes output but does not exit

Use `workflow@5.0.0-beta.33` or later with Nitro v3.
Use `workflow@5.0.0-beta.35` or later with Nitro v3.
38 changes: 19 additions & 19 deletions docs/content/docs/v5/getting-started/react-router/v8.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,19 @@ This integration requires Nitro v3.

<Step>

## Use a shared build directory
## Configure React Router

Set an explicit build directory in your React Router config:
Keep server rendering enabled and set an explicit build directory for the manifest bridge:

```typescript title="react-router.config.ts" lineNumbers
import type { Config } from "@react-router/dev/config";

export default {
ssr: true,
buildDirectory: "build", // [!code highlight]
buildDirectory: "build",
} satisfies Config;
```

React Router will place browser assets in `build/client`. Nitro will place the runnable server in `build/server`.

</Step>

<Step>
Expand Down Expand Up @@ -107,21 +105,27 @@ Update `vite.config.ts`:
```typescript title="vite.config.ts" lineNumbers
import { reactRouter } from "@react-router/dev/vite";
import { nitro } from "nitro/vite";
import { cp } from "node:fs/promises";
import { resolve } from "node:path";
import { defineConfig } from "vite";
import { workflow } from "workflow/vite";
import reactRouterConfig from "./react-router.config";

export default defineConfig({
plugins: [
reactRouter(),
nitro({
serverDir: "./server",
output: {
dir: reactRouterConfig.buildDirectory,
serverDir: `${reactRouterConfig.buildDirectory}/server`,
publicDir: `${reactRouterConfig.buildDirectory}/client`,
nitro({ serverDir: "./server" }),
{
name: "react-router-nitro-manifest",
applyToEnvironment: (environment) => environment.name === "client",
async writeBundle(options) {
await cp(
resolve(options.dir!, ".vite"),
resolve(reactRouterConfig.buildDirectory, "client/.vite"),
{ recursive: true },
);
},
}),
},
workflow({ dirs: ["workflows"] }),
],
environments: {
Expand All @@ -136,7 +140,7 @@ export default defineConfig({
});
```

Keep `dirs: ["workflows"]` so subsequent builds do not scan generated files under `build`. Place `reactRouter()` before `nitro()` in the plugin array.
The small `react-router-nitro-manifest` plugin copies React Router's Vite manifest back to `build/client` after Nitro writes the client build to its deployment output. Keep `dirs: ["workflows"]` so Workflow SDK only scans your source workflow directory. Do not set Nitro's `output.dir`; Nitro uses it to produce the output expected by each deployment preset.

</Step>

Expand Down Expand Up @@ -208,7 +212,7 @@ Build and start the production server:

```bash
pnpm vite build
node ./build/server/index.mjs
node ./.output/server/index.mjs
```

You can inspect local runs with `pnpm workflow web`.
Expand All @@ -223,10 +227,6 @@ You can inspect local runs with `pnpm workflow web`.

Check that the `ssr` environment input points to `./server/ssr.ts`.

### A second build tries to compile files under `build/server`

Use `workflow({ dirs: ["workflows"] })`, remove the existing `build` directory once, and rebuild.

### `vite build` finishes output but does not exit

Use `workflow@5.0.0-beta.33` or later with Nitro v3.
Use `workflow@5.0.0-beta.35` or later with Nitro v3.
Loading