How toUse middleware in React Router
If you're excited to use middleware in React Router, this guide will show you how to enable and implement them starting from version 7.3.0. You'll learn how to intercept requests and enhance loaders and actions on both the server and client.
Enable Middleware in React Router
First, upgrade every React Router package to v7.3.0. The simplest way is to change in your package.json
any @react-router/*
package and the react-router
package itself to that version.
Then go to your React Router config file and add this code.
react-router.config.ts import type { Config } from "@react-router/dev/config"; import "react-router"; declare module "react-router" { interface Future { unstable_middleware: true; // 👈 Enable middleware types } } export default { // ...Other options future: { unstable_middleware: true, // 👈 Enable middleware // ...Other future or unstable flags }, } satisfies Config;
The first thing is to enable unstable_middleware
in the exported config object. This will enable middleware at runtime. The second thing is to add the unstable_middleware
property to the Future
interface in the react-router
module. This will enable middleware types in your code.
Breaking Change for Context
The types are important because middleware comes with a breaking change. Currently, a loader or action receives three arguments:
request
params
context
The request
is a Request
instance, params
is an object with the route params like :userId
, and context
is an object with the interface AppLoadContext
.
This AppLoadContext
interface contains the values the HTTP server (e.g., Express, Hono, Cloudflare Workers, etc.) passes using getLoadContext
to the React Router request handler.
When you enable middleware in your React Router application, this AppLoadContext
is replaced with a new unstable_RouterContextProvider
object. This is essentially a Map
where middleware can store values that loaders or actions can read.
Create a Middleware
Now that you have middleware enabled, you can create a custom middleware like this.
app/middleware/session.ts import type { unstable_MiddlewareFunction, unstable_RouterContextProvider, } from "react-router"; import { unstable_createContext } from "react-router"; const sessionContext = unstable_createContext<Session<Data, FlashData>>(); export const sessionMiddleware: unstable_MiddlewareFunction<Response> = async ( { request, context }, next ) => { let session = await sessionStorage.getSession(request.headers.get("Cookie")); context.set(sessionContext, session); let response = await next(); response.headers.append( "Set-Cookie", await sessionStorage.commitSession(session) ); return response; }; export function getSession(context: unstable_RouterContextProvider) { return context.get(sessionContext); }
This is a simplified implementation of a session middleware that uses React Router session storage to access the session and store it in the unstable_RouterContextProvider
object.
Then we export a simple function to get the session from that context so we don't have to export the sessionContext
object.
Finally, you can add the middleware to a route like this:
app/root.tsx import { sessionMiddleware } from "./middleware/session"; export const unstable_middleware = [sessionMiddleware];
And access the session in a loader or action like this:
app/routes/profile.tsx export async function loader({ context }: Route.LoaderArgs) { let user = getSession(context).get("user"); if (!user) return redirect("/login"); return { user }; }
And that's it! You can now use middleware in your React Router application.
Typed Params in Middleware
So far, we have used only request
and context
in the session middleware. However, it also receives the params
, like a loader or action. This is not typed because the middleware we created is generic.
If we want to create a middleware tied to a specific route, we can use the Route.unstable_MiddlewareFunction
type, which will have the params
typed.
app/root.tsx import type { Route } from "./+types/root"; export const unstable_middleware: Route.unstable_MiddlewareFunction[] = [ async ({ request, params, context }, next) => { // params is typed here return next(); }, ];
Client-Side Middleware
React Router middleware is not only for server loaders and actions—you can also use them for client loaders and actions.
app/routes/profile.tsx export const unstable_clientMiddleware = [someClientMiddleware];
The unstable_clientMiddleware
works like unstable_middleware
, but for client loaders and actions. In this case, the type won't be Route.unstable_MiddlewareFunction
but Route.unstable_ClientMiddlewareFunction
.
The generic type is the same, unstable_MiddlewareFunction
, but instead of unstable_MiddlewareFunction<Response>
, you can just use unstable_MiddlewareFunction
, since the next
function in a client middleware doesn't return anything.