Skip to content

Commit 88d55eb

Browse files
committed
docs(react-native): Document GlobalErrorBoundary for fatal non-render errors
Adds a "Showing a Fallback UI for Fatal Errors" subsection covering Sentry.GlobalErrorBoundary (and the withGlobalErrorBoundary HOC), the includeNonFatalGlobalErrors and includeUnhandledRejections opt-ins, reset semantics, and a migration note replacing the hand-rolled ErrorUtils.setGlobalHandler + DeviceEventEmitter + ErrorBoundary pattern the page previously recommended. Updates the top-of-page warning and the Non-Render Errors intro to link to the new section. The companion SDK change adding the component ships in getsentry/sentry-react-native#6023.
1 parent 1510d7a commit 88d55eb

1 file changed

Lines changed: 84 additions & 52 deletions

File tree

docs/platforms/react-native/integrations/error-boundary.mdx

Lines changed: 84 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The React Native SDK exports an error boundary component that uses [React compon
1212

1313
<Alert level="warning" title="Render errors only">
1414

15-
React error boundaries **only catch errors during rendering, in lifecycle methods, and in constructors**. They do **not** catch errors in event handlers, asynchronous code (`setTimeout`, `Promise`), or native errors. See [Handling Non-Render Errors](#handling-non-render-errors) for how to handle those.
15+
React error boundaries **only catch errors during rendering, in lifecycle methods, and in constructors**. They do **not** catch errors in event handlers, asynchronous code (`setTimeout`, `Promise`), or native errors. For a fallback UI that covers those cases too, use [`Sentry.GlobalErrorBoundary`](#showing-a-fallback-ui-for-fatal-errors).
1616

1717
</Alert>
1818

@@ -102,7 +102,7 @@ In [React v17 and above](https://reactjs.org/blog/2020/08/10/react-v17-rc.html#n
102102

103103
Errors in event handlers, `async` functions, `setTimeout`, and other non-render code won't be caught by `ErrorBoundary`. This is a [React limitation](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary), not specific to Sentry.
104104

105-
Sentry's [`reactNativeErrorHandlersIntegration`](/platforms/react-native/integrations/default/#reactnativeerrorhandlersintegration) (enabled by default) **automatically reports** these errors to Sentry — you don't need to do anything extra for error reporting. However, it won't display a fallback UI.
105+
Sentry's [`reactNativeErrorHandlersIntegration`](/platforms/react-native/integrations/default/#reactnativeerrorhandlersintegration) (enabled by default) **automatically reports** these errors to Sentry — you don't need to do anything extra for error reporting. For a fallback UI on fatal non-render errors, use [`Sentry.GlobalErrorBoundary`](#showing-a-fallback-ui-for-fatal-errors).
106106

107107
### Component-Level Error Handling
108108

@@ -138,74 +138,94 @@ function MyComponent() {
138138
}
139139
```
140140

141-
### Global Error Handling With Fallback UI
141+
### Showing a Fallback UI for Fatal Errors
142142

143-
To show a fallback UI for **any** unhandled JavaScript error (not just render errors), you can create a provider that listens to React Native's global error handler and combines it with `ErrorBoundary`:
143+
To show a fallback UI for fatal JavaScript errors that are thrown **outside** the React render tree — event handlers, `setTimeout`, async code, and errors routed through `ErrorUtils` — wrap your app in `Sentry.GlobalErrorBoundary`:
144144

145145
```javascript
146-
import React, { useState, useEffect } from "react";
147-
import { Button, DeviceEventEmitter, Text, View } from "react-native";
146+
import React from "react";
147+
import { Button, Text, View } from "react-native";
148148
import * as Sentry from "@sentry/react-native";
149149

150-
// Set up the global error listener before Sentry.init()
151-
const globalHandler = global.ErrorUtils?.getGlobalHandler();
152-
global.ErrorUtils?.setGlobalHandler((error, isFatal) => {
153-
DeviceEventEmitter.emit("GLOBAL_UNHANDLED_ERROR", error);
154-
155-
// Call the default handler in development for the React Native red box.
156-
// In production, we skip the default handler to prevent the app from
157-
// crashing so the fallback UI can be shown instead.
158-
if (__DEV__ && globalHandler) {
159-
globalHandler(error, isFatal);
160-
}
161-
});
162-
163-
Sentry.init({
164-
dsn: "___PUBLIC_DSN___",
165-
});
166-
167-
function GlobalFallback({ onReset }) {
150+
function App() {
168151
return (
169-
<View>
170-
<Text>Something went wrong.</Text>
171-
<Button onPress={onReset} title="Restart" />
172-
</View>
152+
<Sentry.GlobalErrorBoundary
153+
fallback={({ error, eventId, resetError }) => (
154+
<View>
155+
<Text>Something went wrong.</Text>
156+
<Text>{error?.message}</Text>
157+
<Text>Event ID: {eventId}</Text>
158+
<Button onPress={resetError} title="Restart" />
159+
</View>
160+
)}
161+
>
162+
<RestOfYourApp />
163+
</Sentry.GlobalErrorBoundary>
173164
);
174165
}
166+
```
175167
176-
function AppErrorProvider({ children }) {
177-
const [globalError, setGlobalError] = useState(null);
168+
`GlobalErrorBoundary` is a superset of `ErrorBoundary`: it catches everything `ErrorBoundary` catches **and** fatal non-rendering errors routed through React Native's `ErrorUtils` global handler. The error is captured through Sentry's normal pipeline (with the correct `mechanism` and `fatal` level) before the fallback is rendered — no duplicate events, no need to call `Sentry.captureException` yourself.
178169
179-
useEffect(() => {
180-
const subscription = DeviceEventEmitter.addListener(
181-
"GLOBAL_UNHANDLED_ERROR",
182-
(error) => setGlobalError(error)
183-
);
184-
return () => subscription.remove();
185-
}, []);
170+
In release builds, `GlobalErrorBoundary` takes over React Native's default fatal handler so the fallback can own the screen instead of the app being torn down. In development, LogBox still appears — the fallback renders alongside it.
186171
187-
if (globalError) {
188-
return <GlobalFallback onReset={() => setGlobalError(null)} />;
189-
}
172+
#### Opt-In Options
190173
191-
return (
192-
<Sentry.ErrorBoundary
193-
fallback={({ resetError }) => (
194-
<GlobalFallback onReset={resetError} />
195-
)}
196-
>
197-
{children}
198-
</Sentry.ErrorBoundary>
199-
);
200-
}
174+
By default, only fatal errors trigger the fallback. Two props extend coverage:
175+
176+
```javascript
177+
<Sentry.GlobalErrorBoundary
178+
// Also render the fallback for non-fatal ErrorUtils errors
179+
includeNonFatalGlobalErrors
180+
// Also render the fallback for unhandled promise rejections
181+
includeUnhandledRejections
182+
fallback={/* ... */}
183+
>
184+
<App />
185+
</Sentry.GlobalErrorBoundary>
201186
```
202187
203-
<Alert level="info" title="Note">
188+
Most apps should leave both off: non-fatals are often recoverable, and unhandled rejections are frequently surfaced as toasts or inline errors rather than as a full-screen fallback.
204189
205-
When intercepting the global error handler, unhandled errors will still be reported to Sentry by the `reactNativeErrorHandlersIntegration`. You do **not** need to call `Sentry.captureException` manually in the global handler.
190+
#### HOC Alternative
191+
192+
```javascript
193+
import * as Sentry from "@sentry/react-native";
194+
195+
export default Sentry.withGlobalErrorBoundary(App, {
196+
fallback: ({ error, resetError }) => (
197+
<Fallback error={error} onRetry={resetError} />
198+
),
199+
});
200+
```
201+
202+
<Alert level="info" title="Reset semantics">
203+
204+
`resetError()` clears the fallback and remounts the children, but it does **not** restore JavaScript state that was corrupted by the error. Use `onReset` to navigate to a safe screen, reload data, or reset your state container.
206205
207206
</Alert>
208207
208+
#### Migration From a Hand-Rolled `setGlobalHandler`
209+
210+
Before `GlobalErrorBoundary`, showing a fallback for non-render errors required overriding `ErrorUtils.setGlobalHandler`, bridging through `DeviceEventEmitter`, and combining with `ErrorBoundary`. This is **no longer recommended** — the manual approach bypasses Sentry's flush and fatal deduplication, risks running the app in a corrupted state, and is fragile across React Native versions.
211+
212+
```javascript
213+
// ❌ Before: manual global handler + ErrorBoundary
214+
const defaultHandler = global.ErrorUtils?.getGlobalHandler();
215+
global.ErrorUtils?.setGlobalHandler((error, isFatal) => {
216+
DeviceEventEmitter.emit("GLOBAL_UNHANDLED_ERROR", error);
217+
if (__DEV__) defaultHandler?.(error, isFatal);
218+
});
219+
// ...plus a provider subscribing to the event and an ErrorBoundary
220+
221+
// ✅ After: one component
222+
<Sentry.GlobalErrorBoundary fallback={/* ... */}>
223+
<App />
224+
</Sentry.GlobalErrorBoundary>
225+
```
226+
227+
If you previously disabled the `onerror` integration to avoid duplicate reports, re-enable it (or remove the `reactNativeErrorHandlersIntegration({ onerror: false })` override) — `GlobalErrorBoundary` relies on it for capture.
228+
209229
## Options
210230
211231
The ErrorBoundary component exposes a variety of props that can be passed in for extra configuration. There aren't any required options, but we highly recommend setting a fallback component.
@@ -230,6 +250,18 @@ A function that gets called on ErrorBoundary `componentWillUnmount()`.
230250
231251
A function that gets called before an error is sent to Sentry, allowing for extra tags or context to be added to the error.
232252
253+
### `GlobalErrorBoundary`-Only Options
254+
255+
`GlobalErrorBoundary` accepts every `ErrorBoundary` option above plus:
256+
257+
`includeNonFatalGlobalErrors` (boolean, default `false`)
258+
259+
Also render the fallback when a non-fatal error is reported through `ErrorUtils`. Off by default to match the semantics of React Native's native red-screen, which only appears for fatals.
260+
261+
`includeUnhandledRejections` (boolean, default `false`)
262+
263+
Also render the fallback when an unhandled promise rejection occurs. Off by default because many apps prefer to surface rejections as toasts or inline errors rather than as a full-screen fallback.
264+
233265
## Examples
234266
235267
### Setting a Fallback Function (Render Props)

0 commit comments

Comments
 (0)