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: 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.
0 commit comments