Use a class Error Boundary to replace a crashed descendant with fallback UI, then report the failure from componentDidCatch(error, info). Keep fallback state changes in static getDerivedStateFromError, normalize the thrown value before serialization, and send the component context to an endpoint you control. This handles rendering failures—not every frontend exception.
Implement the boundary and report hook
React documents two complementary lifecycle methods. getDerivedStateFromError is for the state update that selects fallback content. componentDidCatch runs the side effect, such as forwarding a report to your backend or an error-reporting service.
class ReportedBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(thrownValue, info) {
const error = normalizeThrownValue(thrownValue);
void fetch('/api/client-errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
kind: 'react-render',
message: error.message,
stack: error.stack,
componentStack: info.componentStack,
url: window.location.href,
occurredAt: new Date().toISOString()
}),
keepalive: true
}).catch(() => {
// Use a local fallback logger or queue if delivery matters.
});
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? <p>This section could not be loaded.</p>;
}
return this.props.children;
}
}
function normalizeThrownValue(value) {
if (value instanceof Error) {
return { message: value.message, stack: value.stack };
}
if (value == null) {
return { message: String(value), stack: undefined };
}
if (typeof value === 'string') {
return { message: value, stack: undefined };
}
try {
return { message: JSON.stringify(value), stack: undefined };
} catch {
return { message: 'Non-serializable thrown value', stack: undefined };
}
}
The reporting function in React’s examples is illustrative; React does not provide the endpoint, request schema, retries, queue, authentication, retention policy, or delivery guarantee. Your server should validate payloads, rate-limit requests, and return a clear status. Decide whether a failed report is queued, sampled, or discarded rather than allowing reporting code to create another user-visible failure.
Why normalization is necessary
A JavaScript throw can contain a string, null, an object, or another value instead of an Error. Code that blindly reads error.message or error.stack can therefore fail while preparing the report. Preserve the original error when it is an Error, and serialize other values defensively.
#1 Best Overall
What componentStack adds
info.componentStack identifies the React component path associated with the failure and may include source locations. In production, component names can be minified; source maps can make the component stack and JavaScript stack readable when your deployment and backend retain the necessary mappings. Treat component names, URLs, and any attached metadata as potentially sensitive.
Choose a boundary that matches a recovery region
Place a boundary around a meaningful piece of the interface whose failure has a useful fallback. A conversation list, message panel, route-level view, or dashboard card can often fail independently. Wrapping every avatar or tiny presentational component usually creates noisy boundaries and awkward recovery behavior.
- Put a high-level boundary around a route or page to prevent a single rendering failure from blanking the entire application.
- Add narrower boundaries where the user can continue productively if one panel fails.
- Give the fallback an actionable recovery path, such as retrying data or navigating elsewhere, when that action is safe.
- Do not place the boundary only around the component most likely to fail if its own fallback or lifecycle code could also throw; a boundary does not catch errors in itself.
Know which failures this mechanism observes
| Failure source | Error Boundary coverage | Use instead or in addition |
|---|---|---|
| Descendant rendering, including a child lifecycle method | Yes; fallback state can be rendered and componentDidCatch can report it. |
Boundary reporting endpoint and a suitable fallback. |
Event handlers such as onClick |
No. | Handle or report with try/catch in the handler, plus an application-level error logger where appropriate. |
setTimeout, requestAnimationFrame, and most other asynchronous callbacks |
No. | Catch at the async boundary, reject or handle promises explicitly, and report there. React documents an exception for errors thrown inside a startTransition function returned by useTransition. |
| Server-side rendering | No; the browser boundary does not run around server rendering. | Use the server renderer’s error callbacks. |
| The boundary’s own render or lifecycle code | No. | Use a parent boundary and test the fallback path separately. |
| Errors React recovers from during client rendering or hydration | Not necessarily as a caught descendant failure. | Use the root’s onRecoverableError callback. |
Why a render-time try/catch is not a replacement
A try/catch surrounding JSX or a component call does not reliably intercept exceptions thrown by React while it is rendering the component tree. React’s lint guidance recommends an Error Boundary for child rendering failures because React controls that rendering process.
Report server-rendering failures separately
Streaming server renderers expose their own onError callbacks. Log there even when you provide a custom callback. With Suspense, a server rendering error can cause fallback HTML to be sent while the client retries rendering, so onError may fire even though the stream continues; it is not automatically proof that the whole response failed.
Rank #3
Add root-level telemetry for recoverable errors
React 18’s createRoot and hydrateRoot accept onRecoverableError. Configure it alongside component boundaries to capture problems React recovered from during rendering or hydration:
const root = createRoot(container, {
onRecoverableError(error, errorInfo) {
reportClientError({
kind: 'react-recoverable',
...normalizeThrownValue(error),
componentStack: errorInfo?.componentStack
});
}
});
This callback supplements, rather than replaces, boundary reporting. Check the API reference for the React version installed by your application before relying on version-specific callback signatures.
Rank #4
Design a useful and safe backend payload
Start with fields that help an engineer reproduce and group the failure:
- Failure kind: distinguish boundary, recoverable-root, event, and server reports.
- Error data: normalized message and stack, when present.
- React context:
componentStackand application release or build identifier. - Runtime context: page or route, browser metadata, locale, and timestamp, collected only when justified.
- Correlation data: a request or session identifier that does not expose a user’s identity unnecessarily.
Minimize data before it leaves the browser. Do not include form contents, access tokens, full query strings, or personal data merely because they are available. Redact URLs and user context on the client or at ingestion, authenticate the endpoint as appropriate, enforce size limits, and define retention. Source maps should be protected while still available to the system that decodes production stacks.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsQuick Recap
Best Value
Test the reporting path, not just the fallback
- Render a child that intentionally throws during render in a non-production test build.
- Assert that the boundary displays its fallback and that exactly one report is accepted for the failure.
- Test thrown strings,
null, and non-serializable objects to verify normalization. - Exercise a click-handler error, a timer rejection, server rendering, and hydration separately; confirm they reach their designated mechanisms rather than the boundary endpoint.
- Simulate a failed or slow report request and verify that the fallback remains usable.
- Confirm that release identifiers and source maps let your backend group duplicate failures without retaining unnecessary sensitive data.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




