Skip to content

React Router on Netlify

React Router 7+ can be used as a framework, giving you a server and browser runtime that focuses on performance and excellent user experiences. You get a number of built-in tools to build better websites, such as nested routes, parallel data requests, and robust built-in error handling.

These features provide important benefits for React Router projects, including those built by and deployed with Netlify.

  • Nested routes. By default, React Router creates routes for components that serve as boundaries for data loading and code splitting.
  • Parallel data requests by default. Instead of waiting on sequential requests, React Router processes requests in parallel and then sends a complete HTML document.
  • Built-in global error handling. React Router has built-in error handling for server and client rendering and server side data handling. Error boundaries don’t block the entire page from rendering, only the affected component.
  • Server and client middleware. Run code before or after rendering responses. Middleware simplifies auth, error handling, logging, instrumentation, and more.

To create a React Router app and deploy it on Netlify, use our Netlify starter template with the command: npx create-react-router@latest --template netlify/react-router-template.

You’ll get all you need to deploy to Netlify, including a netlify.toml file with common build settings.

Create a new React Router app to deploy to Netlify

Section titled “Create a new React Router app to deploy to Netlify”

You can use the command line to scaffold a new project based on the Netlify starter template for React Router. This can streamline the process of getting your project up and running.

  1. In your terminal, run npx create-react-router@latest --template netlify/react-router-template.
  2. Follow the interactive prompts.
  3. Follow the starter template README to get your project running.

Update the deploy target for an existing React Router app

Section titled “Update the deploy target for an existing React Router app”

If you have an existing React Router 7+ project that isn’t deployed on Netlify and you want to change the deploy target to Netlify, install Netlify’s React Router Vite plugin and add it to your Vite config:

Terminal window
npm install @netlify/vite-plugin-react-router
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
// ↓ add this
import netlifyReactRouter from "@netlify/vite-plugin-react-router";
export default defineConfig({
plugins: [
reactRouter(),
tsconfigPaths(),
netlifyReactRouter() // ← add this
]
});

By default, @netlify/vite-plugin-react-router deploys your React Router app to Netlify Serverless Functions (Node.js runtime). You can optionally deploy to Netlify Edge Functions (Deno runtime) instead, keeping in mind a few considerations.

First, set the edge option to true in your Vite config:

import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import netlifyReactRouter from "@netlify/vite-plugin-react-router";
export default defineConfig({
plugins: [
reactRouter(),
tsconfigPaths(),
netlifyReactRouter({ edge: true }) // ← deploy to Edge Functions
]
});

Second, you must provide an app/entry.server.tsx (or .jsx) file. Create a file with the following content:

export { default } from 'virtual:netlify-server-entry'

Finally, if you have your own Netlify Serverless Functions (typically in netlify/functions) for which you’ve configured a path, you must exclude those paths to avoid conflicts with the generated React Router SSR handler:

export default defineConfig({
plugins: [
reactRouter(),
tsconfigPaths(),
netlifyReactRouter({
edge: true,
excludedPaths: ['/ping', '/api/*', '/webhooks/*']
})
]
});

On your next deploy, page renders, loaders, and actions will run on Netlify’s global edge network.

Before deploying to Edge Functions, review the Netlify Edge Functions documentation for important details:

Moving back from Edge Functions to Serverless Functions

Section titled “Moving back from Edge Functions to Serverless Functions”

To switch from Edge Functions back to Serverless Functions, you must:

  1. Remove the edge: true option from your vite.config.ts
  2. Delete the app/entry.server.tsx file (React Router will use its default Node.js-compatible entry)

With the Netlify Vite plugin, your React Router dev server gives you parity with Netlify’s production environment. This gives you the same platform primitives locally that your site uses in production.

With the module enabled, you or an agent can access and build with the following Netlify features when running react-router dev:

  • Serverless functions
  • Edge functions
  • Blobs
  • Cache API
  • Image CDN
  • Redirects & rewrites
  • Headers
  • Environment variables

Install the Netlify Vite plugin:

Terminal window
npm install -D @netlify/vite-plugin

Then add it to your project’s Vite config file:

import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import netlifyReactRouter from "@netlify/vite-plugin-react-router";
// ↓ add this
import netlify from "@netlify/vite-plugin";
export default defineConfig({
plugins: [
reactRouter(),
tsconfigPaths(),
netlifyReactRouter(),
netlify() // ← add this
]
});

React Router introduced a stable middleware feature in v7.9.0.

To use middleware, opt in to the feature via future.v8_middleware and follow the docs.

This requires requires v2.0.0+ of @netlify/vite-plugin-react-router.

You can access the Netlify context in your middleware by using the fully type-safe RouterContext Netlify provides:

import { netlifyRouterContext } from "@netlify/vite-plugin-react-router/serverless"
// NOTE: if setting `edge: true`, import from /edge instead
import type { Route } from "./+types/home"
const logMiddleware: Route.MiddlewareFunction = async ({ request, context }) => {
const country = context.get(netlifyRouterContext).geo?.country?.name ?? "unknown"
console.log(`Handling ${request.method} request to ${request.url} from ${country}`)
}
export const middleware: Route.MiddlewareFunction[] = [logMiddleware]
export default function Home() {
return <h1>Home</h1>
}