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
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -379,7 +379,7 @@ Date: YYYY-MM-DD
379
379
380
380
## v7.9.5
381
381
382
-
Date: 2025-10-27
382
+
Date: 2025-10-29
383
383
384
384
### What's Changed
385
385
@@ -441,7 +441,7 @@ This release includes a new `unstable_useRoute()` hook that provides a type-safe
441
441
### Patch Changes
442
442
443
443
- `@react-router/dev` - Update `valibot` dependency to `^1.1.0` ([#14379](https://github.com/remix-run/react-router/pull/14379))
444
-
- `@react-router/node` - Validate format of incoming session ids ([#14426](https://github.com/remix-run/react-router/pull/14426))
444
+
- `@react-router/node` - Validate format of incoming session ids in `createFileSessionStorage` ([#14426](https://github.com/remix-run/react-router/pull/14426))
Copy file name to clipboardExpand all lines: GOVERNANCE.md
+23-1Lines changed: 23 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -107,16 +107,17 @@ This table gives a high-level overview of the stages, but please see the individ
107
107
### Stage 1 — Consideration
108
108
109
109
- A proposal enters **Stage 1 — Consideration** when 2 SC members indicate interest/support for the idea as a valuable addition to React Router
110
-
- Upon entering this stage, a GitHub Issue will be created for the feature and added to the roadmap
111
110
- These initial supporting SC members will be the champions for the feature and will be loosely responsible for shepherding the feature through the stages of the RFC process
112
111
- At this stage, the proposal is eligible for a sample PR implementation from a core team or community member
113
112
- The SC will indicate at this stage if this is a feature open to a community PR or something the core team would prefer to tackle
113
+
- We will add the `accepting-prs` label to the RFC if we are open to community PRs
114
114
- All PRs at this stage should implement the feature in an "unstable" fashion (usually using an `unstable_` prefix on the future flag or API)
115
115
116
116
### Stage 2 — Alpha
117
117
118
118
- A proposal enters **Stage 2 — Alpha** once a PR has been opened implementing the feature in an `unstable_` state
119
119
- At this stage, we should open an Issue for the Proposal and add it to the [Roadmap](https://github.com/orgs/remix-run/projects/5)
120
+
- We will remove any `accepting-prs` label and add the `🗺️ Roadmap` label to indicate that this RFc is officially on the roadmap
120
121
- At this stage, we are looking for early community testing _before_ merging any work to the React Router repo — so these PRs should provide a mechanism for community members to opt into to alpha testing
121
122
- Maintainers can trigger an alpha release from the PR branch by adding the `alpha-release` label, which will kick off an experimental release and comment it back on the PR
122
123
- Because the alpha release may contain other work committed to `dev` but not yet released in a stable version, it may not be ideal for testing in all cases
@@ -225,3 +226,24 @@ Matt Brophy, Bryan Ross (rossipedia), Mark Dalgleish, and Pedro Cattori discusse
225
226
- Matt Brophy will try to pick up the [`<Link onPrefetch>`](https://github.com/remix-run/react-router/discussions/12375) task soon
226
227
- Matt Brophy and Pedro Cattori will sync up offline to figure out what parts of the consolidated hook can be done better with typegen and decide on the requirements ([RFC](https://github.com/remix-run/react-router/issues/13073))
227
228
</details>
229
+
230
+
<details>
231
+
<summary>2025-11-04 Meeting Notes</summary>
232
+
233
+
The SC reviewed items on the open Proposal for React Router v8
234
+
235
+
- Confirmed the plan to drop CJS builds for ESM-only builds
236
+
- We will plan RR v8 for Q2 2026 which aligns nicely with the EOL for Node 20
237
+
- v8 will have a minimum Node version of 22.12 so that the `require(esm)` feature is not behind an [experimental flag](https://nodejs.org/docs/latest-v22.x/api/modules.html#loading-ecmascript-modules-using-require)
238
+
- Going forward we will aim for a yearly major release in the same Q2 timeframe
239
+
- We would like to try to get `useRouterState` into v8 as the other half of the `unstable_useRoute` coin
240
+
- We think Subresource Integrity (SRI) is ready for stabilization but we would like to ping a few existing users and/or SME's to confirm the implementation is valid
241
+
- Discussed the `unstable_optimizedDeps` feature, confirming it will remain unstable in V8 and then be pseudo-deprecated in favor of RollDown
242
+
- There are some concerns about RollDown's full bundle mode limiting scalability so we may need to wait until rolldown is ready for testing
243
+
- Decided against making "type-safe matches" an immediate V8 necessity due to the API churn
244
+
- RSC implementation will not have a stable API ready for V8 but will be released in a minor version later
245
+
- We will not be deprecating existing APIs at that time because not everyone should have to use RSC
246
+
-`Vite environment API` and `split route modules` are nearing stabilization
247
+
- Reviewed a new RFC to stop URL normalization in loaders
Copy file name to clipboardExpand all lines: docs/api/framework-conventions/entry.server.tsx.md
+14-12Lines changed: 14 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,17 +9,18 @@ order: 5
9
9
10
10
## Summary
11
11
12
-
<docs-info>
13
-
This file is optional
14
-
</docs-info>
15
-
16
12
This file is the server-side entry point that controls how your React Router application generates HTTP responses on the server.
17
13
18
14
This module should render the markup for the current page using a [`<ServerRouter>`][serverrouter] element with the `context` and `url` for the current request. This markup will (optionally) be re-hydrated once JavaScript loads in the browser using the [client entry module][client-entry].
19
15
16
+
<docs-info>This file is optional if you are running on Node. If it is not present, a [default implementation][node-streaming-entry-server] will be used.
17
+
<br/>
18
+
<br/>
19
+
If you are using another runtime (i.e., Cloudflare) then you need to include this file. You can find sample implementations in the [templates repository][templates-repo].</docs-info>
20
+
20
21
## Generating `entry.server.tsx`
21
22
22
-
By default, React Router will handle generating the HTTP Response for you. You can reveal the default entry server file with the following:
23
+
When running in Node, React Router will handle generating the HTTP Response for you. You can reveal the default entry server file with the following:
23
24
24
25
```shellscript nonumber
25
26
npx react-router reveal
@@ -42,7 +43,7 @@ export default function handleRequest(
42
43
request:Request,
43
44
responseStatusCode:number,
44
45
responseHeaders:Headers,
45
-
routerContext:EntryContext
46
+
routerContext:EntryContext,
46
47
) {
47
48
returnnewPromise((resolve, reject) => {
48
49
const { pipe, abort } =renderToPipeableStream(
@@ -62,15 +63,15 @@ export default function handleRequest(
62
63
newResponse(stream, {
63
64
headers: responseHeaders,
64
65
status: responseStatusCode,
65
-
})
66
+
}),
66
67
);
67
68
68
69
pipe(body);
69
70
},
70
71
onShellError(error:unknown) {
71
72
reject(error);
72
73
},
73
-
}
74
+
},
74
75
);
75
76
});
76
77
}
@@ -113,7 +114,7 @@ export function handleDataRequest(
113
114
request,
114
115
params,
115
116
context,
116
-
}:LoaderFunctionArgs|ActionFunctionArgs
117
+
}:LoaderFunctionArgs|ActionFunctionArgs,
117
118
) {
118
119
response.headers.set("X-Custom-Header", "value");
119
120
returnresponse;
@@ -131,7 +132,7 @@ export function handleError(
131
132
request,
132
133
params,
133
134
context,
134
-
}:LoaderFunctionArgs|ActionFunctionArgs
135
+
}:LoaderFunctionArgs|ActionFunctionArgs,
135
136
) {
136
137
if (!request.signal.aborted) {
137
138
sendErrorToErrorReportingService(error);
@@ -155,8 +156,9 @@ For an example, please refer to the default [`entry.server.tsx`][node-streaming-
155
156
Note that this does not handle thrown `Response` instances from your `loader`/`action` functions. The intention of this handler is to find bugs in your code which result in unexpected thrown errors. If you are detecting a scenario and throwing a 401/404/etc. `Response` in your `loader`/`action` then it's an expected flow that is handled by your code. If you also wish to log, or send those to an external service, that should be done at the time you throw the response.
Copy file name to clipboardExpand all lines: docs/how-to/error-boundary.md
+115-3Lines changed: 115 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,7 +21,13 @@ All applications should at a minimum export a root error boundary. This one hand
21
21
- Instances of errors with a stack trace
22
22
- Randomly thrown values
23
23
24
-
```tsx filename=root.tsx
24
+
### Framework Mode
25
+
26
+
[modes: framework]
27
+
28
+
In [Framework Mode][picking-a-mode], errors are passed to the route-level error boundary as a prop (see [`Route.ErrorBoundaryProps`][type-safety]), so you don't need to use a hook to grab it:
29
+
30
+
```tsx filename=root.tsx lines=[1,3-5]
25
31
import { Route } from"./+types/root";
26
32
27
33
exportfunction ErrorBoundary({
@@ -51,8 +57,57 @@ export function ErrorBoundary({
51
57
}
52
58
```
53
59
60
+
### Data Mode
61
+
62
+
[modes: data]
63
+
64
+
In [Data Mode][picking-a-mode], the `ErrorBoundary` doesn't receive props, so you can access it via `useRouteError`:
65
+
66
+
```tsx lines=[1,6,16]
67
+
import { useRouteError } from"react-router";
68
+
69
+
let router =createBrowserRouter([
70
+
{
71
+
path: "/",
72
+
ErrorBoundary: RootErrorBoundary,
73
+
Component: Root,
74
+
},
75
+
]);
76
+
77
+
function Root() {
78
+
/* ... */
79
+
}
80
+
81
+
function RootErrorBoundary() {
82
+
let error =useRouteError();
83
+
if (isRouteErrorResponse(error)) {
84
+
return (
85
+
<>
86
+
<h1>
87
+
{error.status}{error.statusText}
88
+
</h1>
89
+
<p>{error.data}</p>
90
+
</>
91
+
);
92
+
} elseif (errorinstanceofError) {
93
+
return (
94
+
<div>
95
+
<h1>Error</h1>
96
+
<p>{error.message}</p>
97
+
<p>The stack trace is:</p>
98
+
<pre>{error.stack}</pre>
99
+
</div>
100
+
);
101
+
} else {
102
+
return <h1>Unknown Error</h1>;
103
+
}
104
+
}
105
+
```
106
+
54
107
## 2. Write a bug
55
108
109
+
[modes: framework,data]
110
+
56
111
It's not recommended to intentionally throw errors to force the error boundary to render as a means of control flow. Error Boundaries are primarily for catching unintentional errors in your code.
57
112
58
113
```tsx
@@ -67,6 +122,8 @@ This is not just for loaders, but for all route module APIs: loaders, actions, c
67
122
68
123
## 3. Throw data in loaders/actions
69
124
125
+
[modes: framework,data]
126
+
70
127
There are exceptions to the rule in #2, especially 404s. You can intentionally `throw data()` (with a proper status code) to the closest error boundary when your loader can't find what it needs to render the page. Throw a 404 and move on.
71
128
72
129
```tsx
@@ -85,7 +142,13 @@ This will render the `isRouteErrorResponse` branch of the UI from step 1.
85
142
86
143
## 4. Nested error boundaries
87
144
88
-
When an error is thrown, the "closest error boundary" will be rendered. Consider these nested routes:
145
+
When an error is thrown, the "closest error boundary" will be rendered.
146
+
147
+
### Framework Mode
148
+
149
+
[modes: framework]
150
+
151
+
Consider these nested routes:
89
152
90
153
```tsx filename="routes.ts"
91
154
// ✅ has error boundary
@@ -110,10 +173,59 @@ The following table shows which error boundary will render given the origin of t
110
173
| invoice-page.tsx | invoice-page.tsx |
111
174
| payments.tsx | invoice-page.tsx |
112
175
176
+
### Data Mode
177
+
178
+
[modes: data]
179
+
180
+
In Data Mode, the equivalent route tree might look like:
181
+
182
+
```tsx
183
+
let router =createBrowserRouter([
184
+
{
185
+
path: "/app",
186
+
Component: App,
187
+
ErrorBoundary: AppErrorBoundary, // ✅ has error boundary
188
+
children: [
189
+
{
190
+
path: "invoices",
191
+
Component: Invoices, // ❌ no error boundary
192
+
children: [
193
+
{
194
+
path: ":id",
195
+
Component: Invoice,
196
+
ErrorBoundary: InvoiceErrorBoundary, // ✅ has error boundary
197
+
children: [
198
+
{
199
+
path: "payments",
200
+
Component: Payments, // ❌ no error boundary
201
+
},
202
+
],
203
+
},
204
+
],
205
+
},
206
+
],
207
+
},
208
+
]);
209
+
```
210
+
211
+
The following table shows which error boundary will render given the origin of the error:
212
+
213
+
| error origin | rendered boundary |
214
+
| ------------ | ---------------------- |
215
+
|`App`|`AppErrorBoundary`|
216
+
|`Invoices`|`AppErrorBoundary`|
217
+
|`Invoice`|`InvoiceErrorBoundary`|
218
+
|`Payments`|`InvoiceErrorBoundary`|
219
+
113
220
## Error Sanitization
114
221
115
-
In production mode, any errors that happen on the server are automatically sanitized before being sent to the browser to prevent leaking any sensitive server information (like stack traces).
222
+
[modes: framework]
223
+
224
+
In Framework Mode when building for production, any errors that happen on the server are automatically sanitized before being sent to the browser to prevent leaking any sensitive server information (like stack traces).
116
225
117
226
This means that a thrown `Error` will have a generic message and no stack trace in production in the browser. The original error is untouched on the server.
118
227
119
228
Also note that data sent with `throw data(yourData)` is not sanitized as the data there is intended to be rendered.
Copy file name to clipboardExpand all lines: docs/start/framework/route-module.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -480,7 +480,7 @@ The meta of the last matching route is used, allowing you to override parent rou
480
480
481
481
## `shouldRevalidate`
482
482
483
-
In framework mode with SSR, route loaders are automatically revalidated after all navigations and form submissions (this is different from [Data Mode][data-mode-should-revalidate]). This enables middleware and loaders to share a request context and optimize in different ways than then they would be in Data Mode.
483
+
In framework mode with SSR, route loaders are automatically revalidated after all navigations and form submissions (this is different from [Data Mode][data-mode-should-revalidate]). This enables middleware and loaders to share a request context and optimize in different ways than they would in Data Mode.
484
484
485
485
Defining this function allows you to opt out of revalidation for a route loader for navigations and form submissions.
0 commit comments