You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<docs-warning>The middleware feature is currently experimental and subject to breaking changes. Use the `future.unstable_middleware` flag to enable it.</docs-warning>
9
14
10
15
Middleware allows you to run code before and after the `Response` generation for the matched path. This enables common patterns like authentication, logging, error handling, and data preprocessing in a reusable way.
@@ -23,7 +28,9 @@ For example, on a `GET /parent/child` request, the middleware would run in the f
23
28
- Root middleware end
24
29
```
25
30
26
-
## Quick Start
31
+
<docs-info>There are some slight differences between middleware on the server (framework mode) versus the client (framework/data mode). For the purposes of this document, we'll be referring to Server Middleware in most of our examples as it's the most familiar to users who've used middleware in other HTTP servers in the past. Please refer to the [Server vs Client Middleware][server-client] section below for more information.</docs-info>
32
+
33
+
## Quick Start (Framework mode)
27
34
28
35
### 1. Enable the middleware flag
29
36
@@ -39,7 +46,7 @@ export default {
39
46
} satisfiesConfig;
40
47
```
41
48
42
-
<docs-warning>By enabling the middleware feature, you change the type of the `context` parameter to your loaders and actions. Please pay attention to the section on [getLoadContext](#changes-to-getloadcontextapploadcontext) below if you are actively using `context` today.</docs-warning>
49
+
<docs-warning>By enabling the middleware feature, you change the type of the `context` parameter to your loaders and actions. Please pay attention to the section on [getLoadContext][getloadcontext] below if you are actively using `context` today.</docs-warning>
43
50
44
51
### 2. Create a context
45
52
@@ -61,29 +68,27 @@ import { redirect } from "react-router";
@@ -105,7 +110,7 @@ export default function Dashboard({
105
110
}
106
111
```
107
112
108
-
####4. Update your `getLoadContext` function (if applicable)
113
+
### 4. Update your `getLoadContext` function (if applicable)
109
114
110
115
If you're using a custom server and a `getLoadContext` function, you will need to update your implementation to return an instance of `unstable_RouterContextProvider`, instead of a JavaScript object:
111
116
@@ -126,19 +131,211 @@ function getLoadContext(req, res) {
126
131
}
127
132
```
128
133
134
+
## Quick Start (Data Mode)
135
+
136
+
### 1. Enable the middleware flag
137
+
138
+
```tsx
139
+
const router =createBrowserRouter(routes, {
140
+
future: {
141
+
unstable_middleware: true,
142
+
},
143
+
});
144
+
```
145
+
146
+
### 2. Create a context
147
+
148
+
Middleware uses a `context` provider instance to provide data down the middleware chain.
149
+
You can create type-safe context objects using `unstable_createContext`:
### 4. Add an `unstable_getContext()` function (optional)
220
+
221
+
If you wish to include a base context on all navigations/fetches, you can add an `unstable_getContext` function to your router. This will be called to populate a fresh context on every navigation/fetch.
222
+
223
+
```tsx
224
+
let sessionContext =unstable_createContext();
225
+
226
+
const router =createBrowserRouter(routes, {
227
+
future: {
228
+
unstable_middleware: true,
229
+
},
230
+
unstable_getContext() {
231
+
let context =newunstable_RouterContextProvider();
232
+
context.set(sessionContext, getSession());
233
+
returncontext;
234
+
},
235
+
});
236
+
```
237
+
129
238
## Core Concepts
130
239
131
240
### Server vs Client Middleware
132
241
133
-
**Server middleware** (`unstable_middleware`) runs on the server for:
242
+
Server middlewareruns on the server in Framework mode for HTML Document requests and `.data` requests for subsequent navigations and fetcher calls.
134
243
135
-
- HTML Document requests
136
-
-`.data` requests for subsequent navigations and fetcher calls
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:
It is very important to understand _when_ your middlewares will run to make sure your application is behaving as you intend.
286
+
287
+
#### Server Middleware
288
+
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.
290
+
291
+
This raises the question of what is a "handler" in React Router? Is it the route? Or the loader? We think "it depends":
292
+
293
+
- On document requests (`GET /route`), the handler is the route - because the response encompasses both the loader and the route component
294
+
- On data requests (`GET /route.data`) for client-side navigations, the handler is the `loader`/`action`, because that's all that is included in the response
295
+
296
+
Therefore:
297
+
298
+
- Document requests run server middleware whether loaders exist or not because we're still in a "handler" to render the UI
299
+
- Client-side navigations will only run server middleware if a `.data` request is made to the server for a `loader`/`action`
300
+
301
+
This is important behavior for request-annotation middlewares such as logging request durations, checking/setting sessions, setting outgoing caching headers, etc. It would be useless to go to the server and run those types of middlewares when there was no reason to go to the server in the first place. This would result in increased server load and noisy server logs.
302
+
303
+
```tsx filename=app/root.tsx
304
+
// This middleware won't run on client-side navigations without a `.data` request
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.
// By adding a loader, we force the authMiddleware to run on every client-side
329
+
// navigation involving this route.
330
+
exportfunction loader() {
331
+
returnnull;
332
+
}
333
+
```
334
+
335
+
#### Client Middleware
336
+
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.
338
+
142
339
### Context API
143
340
144
341
The new context system provides type safety and prevents naming conflicts:
@@ -428,31 +625,5 @@ export async function loader({
428
625
}
429
626
```
430
627
431
-
## Client-Side Middleware
432
-
433
-
Client middleware works similar to server-side middleware but doesn't return responses because it's not running ion response to an HTTP `Request`:
Copy file name to clipboardExpand all lines: docs/start/data/route-object.md
+48Lines changed: 48 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -54,6 +54,53 @@ function MyRouteComponent() {
54
54
}
55
55
```
56
56
57
+
## `unstable_middleware`
58
+
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.
0 commit comments