Skip to content

Commit 9dd7730

Browse files
committed
Middleware docs updates
1 parent 519d069 commit 9dd7730

File tree

5 files changed

+27
-31
lines changed

5 files changed

+27
-31
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ The biggest set of changes in `7.8.0` are to the `unstable_middleware` API's as
414414

415415
⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
416416

417-
- `react-router` - **Middleware**: Change the `unstable_getContext` signature on `RouterProvider`/`HydratedRouter`/`unstable_RSCHydratedRouter` so that it returns an `unstable_RouterContextProvider` instance instead of a `Map` used to construct the instance internally ([#14097](https://github.com/remix-run/react-router/pull/14097))
417+
- `react-router` - **Middleware**: Change the `unstable_getContext` signature on `RouterProvider`, `HydratedRouter`, and `unstable_RSCHydratedRouter` so that it returns an `unstable_RouterContextProvider` instance instead of a `Map` used to construct the instance internally ([#14097](https://github.com/remix-run/react-router/pull/14097))
418418
- ⚠️ This is a breaking change if you have adopted the `unstable_getContext` prop
419419
- `react-router` - **Middleware**: Run client middleware on client navigations even if no loaders exist ([#14106](https://github.com/remix-run/react-router/pull/14106))
420420
- `react-router` - **Middleware**: Convert internal middleware implementations to use the new `unstable_generateMiddlewareResponse` API ([#14103](https://github.com/remix-run/react-router/pull/14103))

docs/how-to/middleware.md

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ import { createDb } from "./db";
125125

126126
function getLoadContext(req, res) {
127127
- return { db: createDb() };
128-
+ return new unstable_RouterContextProvider(
129-
+ new Map([[dbContext, createDb()]])
130-
+ );
128+
+ const context = new unstable_RouterContextProvider();
129+
+ context.set(dbContext, createDb());
130+
+ return context;
131131
}
132132
```
133133

@@ -148,7 +148,7 @@ const router = createBrowserRouter(routes, {
148148
Middleware uses a `context` provider instance to provide data down the middleware chain.
149149
You can create type-safe context objects using `unstable_createContext`:
150150

151-
```ts filename=app/context.ts
151+
```ts
152152
import { unstable_createContext } from "react-router";
153153
import type { User } from "~/types";
154154

@@ -235,13 +235,13 @@ const router = createBrowserRouter(routes, {
235235
});
236236
```
237237

238+
<docs-info>This API exists to mirror the `getLoadContext` API on the server in Framework Mode, which exists as a way to hand off values from your HTTP server to the React Router handler. This `unstable_getContext` API can be used to hand off global values from the `window`/`document` to React Router, but because they're all running in the same context (the browser), you can achieve effectively the the same behavior with a root route middleware. Therefore, you may not need this API the same way you would on the server - but it's provided for consistency.</docs-warning>
239+
238240
## Core Concepts
239241

240242
### Server vs Client Middleware
241243

242-
Server middleware runs on the server in Framework mode for HTML Document requests and `.data` requests for subsequent navigations and fetcher calls.
243-
244-
Because server middleware runs on the server in response to an HTTP `Request`, it returns an HTTP `Response` back up the middleware chain via the `next` function:
244+
Server middleware runs on the server in Framework mode for HTML Document requests and `.data` requests for subsequent navigations and fetcher calls. Because server middleware runs on the server in response to an HTTP `Request`, it returns an HTTP `Response` back up the middleware chain via the `next` function:
245245

246246
```ts
247247
async function serverMiddleware({ request }, next) {
@@ -251,14 +251,11 @@ async function serverMiddleware({ request }, next) {
251251
return response;
252252
}
253253

254+
// Framework mode only
254255
export const unstable_middleware = [serverMiddleware];
255256
```
256257

257-
**Client middleware** (`unstable_clientMiddleware`) runs in the browser in framework and data mode for:
258-
259-
- Client-side navigations and fetcher calls
260-
261-
Client middleware is different because there's no HTTP Request, so it doesn't bubble up anything via the `next` function:
258+
Client middleware runs in the browser in framework and data mode for client-side navigations and fetcher calls. Client middleware is different because there's no HTTP Request, so it doesn't bubble up anything via the `next` function:
262259

263260
```ts
264261
async function clientMiddleware({ request }, next) {
@@ -271,7 +268,7 @@ async function clientMiddleware({ request }, next) {
271268
// Framework mode
272269
export const unstable_clientMiddleware = [clientMiddleware];
273270

274-
// Data mode
271+
// Or, Data mode
275272
const route = {
276273
path: "/",
277274
unstable_middleware: [clientMiddleware],
@@ -286,7 +283,7 @@ It is very important to understand _when_ your middlewares will run to make sure
286283

287284
#### Server Middleware
288285

289-
In a hydrated Framework Mode app, server middleware is designed such that it prioritizes SPA behavior and does not create new network activity by default. Middleware wraps _existing_ request and only runs when you _need_ to hit the server.
286+
In a hydrated Framework Mode app, server middleware is designed such that it prioritizes SPA behavior and does not create new network activity by default. Middleware wraps _existing_ requests and only runs when you _need_ to hit the server.
290287

291288
This raises the question of what is a "handler" in React Router? Is it the route? Or the loader? We think "it depends":
292289

@@ -314,7 +311,7 @@ function loggingMiddleware({ request }, next) {
314311
export const unstable_middleware = [loggingMiddleware];
315312
```
316313

317-
However, there may be cases where you _want_ to run certain middlewares on _every_ client-navigation - even if no loader exists. For example, a form in the authenticated section of your site that doesn't require a `loader` but you'd rather use auth middleware to redirect users away before they fill out the form - rather then when they submit to the `action`. If your middleware meets this criteria, then you can put a `loader` on the route that contains the middleware to force it to always call the server for client side navigations involving that route.
314+
However, there may be cases where you _want_ to run certain server middlewares on _every_ client-navigation - even if no loader exists. For example, a form in the authenticated section of your site that doesn't require a `loader` but you'd rather use auth middleware to redirect users away before they fill out the form - rather then when they submit to the `action`. If your middleware meets this criteria, then you can put a `loader` on the route that contains the middleware to force it to always call the server for client side navigations involving that route.
318315

319316
```tsx filename=app/_auth.tsx
320317
function authMiddleware({ request }, next) {
@@ -334,11 +331,11 @@ export function loader() {
334331

335332
#### Client Middleware
336333

337-
Client middleware is simpler because since we are already on the client and are always making a "request" to the router when navigating, client middlewares will run on every client navigation, regardless of whether or not there are loaders to run.
334+
Client middleware is simpler because since we are already on the client and are always making a "request" to the router when navigating. Client middlewares will run on every client navigation, regardless of whether or not there are loaders to run.
338335

339336
### Context API
340337

341-
The new context system provides type safety and prevents naming conflicts:
338+
The new context system provides type safety and prevents naming conflicts and allows you to provide data to nested middlewares and loader/action functions. In Framework Mode, this replaces the previous `AppLoadContext` API.
342339

343340
```ts
344341
// ✅ Type-safe
@@ -393,7 +390,7 @@ const authMiddleware = async ({ request, context }) => {
393390

394391
### `next()` and Error Handling
395392

396-
React Router contains built-in error handling via the route [`ErrorBoundary`](../start/framework/route-module#errorboundary) export. Just like when a loader/acton throws, if a middleware throws an error it will be caught and handled at the appropriate `ErrorBoundary` and the `Response` will be returned through the ancestor `next()` call. This means that the `next()` function should never throw and should always return a `Response`, so you don't need to worry about wrapping it in a try/catch.
393+
React Router contains built-in error handling via the route [`ErrorBoundary`](../start/framework/route-module#errorboundary) export. Just like when a loader/action throws, if a middleware throws an error it will be caught and handled at the appropriate `ErrorBoundary` and the `Response` will be returned through the ancestor `next()` call. This means that the `next()` function should never throw and should always return a `Response`, so you don't need to worry about wrapping it in a try/catch.
397394

398395
This behavior is important to allow middleware patterns such as automatically setting required headers on outgoing responses (i.e., committing a session) from a root middleware. If any error from a middleware caused `next()` to `throw`, we'd miss the execution of ancestor middlewares on the way out and those required headers wouldn't be set.
399396

@@ -451,9 +448,9 @@ import { createDb } from "./db";
451448

452449
function getLoadContext(req, res) {
453450
- return { db: createDb() };
454-
+ return new unstable_RouterContextProvider(
455-
+ new Map([[dbContext, createDb()]])
456-
+ );
451+
+ const context = new unstable_RouterContextProvider();
452+
+ context.set(dbContext, createDb());
453+
+ return context;
457454
}
458455
```
459456

docs/start/data/route-object.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function MyRouteComponent() {
5656

5757
## `unstable_middleware`
5858

59-
Route middleware runs sequentially before and after navigations. This gives you a singular place to do things like logging and authentication. The `next` function continues down the chain, and on the leaf route the `next` function executes the loaders/actions for the navigation.
59+
Route [middleware][middleware] runs sequentially before and after navigations. This gives you a singular place to do things like logging and authentication. The `next` function continues down the chain, and on the leaf route the `next` function executes the loaders/actions for the navigation.
6060

6161
```tsx
6262
createBrowserRouter([

docs/start/framework/route-module.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ export default function MyRouteComponent({
8080

8181
## `unstable_middleware`
8282

83-
Route middleware runs sequentially on the server before and after document and
83+
Route [middleware][middleware] runs sequentially on the server before and after document and
8484
data requests. This gives you a singular place to do things like logging,
85-
authentication, and post-processing of responses.
85+
authentication, and post-processing of responses. The `next` function continues down the chain, and on the leaf route the `next` function executes the loaders/actions for the navigation.
8686

8787
Here's an example middleware to log requests on the server:
8888

packages/react-router/lib/router/utils.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,10 @@ export interface unstable_RouterContext<T = unknown> {
124124

125125
/**
126126
* Creates a type-safe {@link unstable_RouterContext} object that can be used to
127-
* * store and retrieve arbitrary values in [`action`](../../start/framework/route-module#action)s,
128-
* * [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
129-
* * Similar to React's [`createContext`](https://react.dev/reference/react/createContext),
130-
* * but specifically designed for React Router's request/response lifecycle.
131-
*
132-
* <docs-warning>Enable this API with the `future.unstable_middleware` flag.</docs-warning>
127+
* store and retrieve arbitrary values in [`action`](../../start/framework/route-module#action)s,
128+
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
129+
* Similar to React's [`createContext`](https://react.dev/reference/react/createContext),
130+
* but specifically designed for React Router's request/response lifecycle.
133131
*
134132
* If a `defaultValue` is provided, it will be returned from `context.get()`
135133
* when no value has been set for the context. Otherwise, reading this context
@@ -201,6 +199,7 @@ export function unstable_createContext<T>(
201199
* const userContext = unstable_createContext<User | null>(null);
202200
* const contextProvider = new unstable_RouterContextProvider();
203201
* contextProvider.set(userContext, getUser());
202+
* // ^ Type-safe
204203
* const user = contextProvider.get(userContext);
205204
* // ^ User
206205
*

0 commit comments

Comments
 (0)