React course menu
React Error Boundaries: Catching Rendering Errors
Stop one broken component from crashing your whole React app: what happens on a rendering error, writing an error boundary class component, fallback UI with a retry button, where to place boundaries, what boundaries do not catch, and the react-error-boundary library.
On this page
If a component throws an error while rendering, React removes the whole app from the screen, leaving a blank page, because it cannot trust any part of a broken tree. An error boundary catches errors from the components inside it and shows a fallback instead, so the rest of the app keeps working.
Writing an error boundary#
Error boundaries are one of the few things that still require a class component, because they use two class lifecycle methods:
static getDerivedStateFromError(error): returns new state so the next render shows the fallback;componentDidCatch(error, info): for side effects such as logging the error to a monitoring service.
import { Component, useState } from "react";
class ErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error };
}
componentDidCatch(error, info) {
// In a real app, send this to an error-reporting service.
console.error("Caught by boundary:", error.message);
}
render() {
if (this.state.error) {
return (
<div style={{ background: "#fff5f5", border: "1px solid #ffc9c9", padding: 10, borderRadius: 6 }}>
<p style={{ margin: "0 0 6px" }}>⚠️ This widget crashed: {this.state.error.message}</p>
<button onClick={() => this.setState({ error: null })}>Try again</button>
</div>
);
}
return this.props.children;
}
}
function RiskyCounter() {
const [count, setCount] = useState(0);
if (count === 3) throw new Error("Count reached 3!");
return <button onClick={() => setCount(count + 1)}>Count: {count} (crashes at 3)</button>;
}
export default function App() {
return (
<div>
<h4 style={{ margin: "0 0 8px" }}>Dashboard</h4>
<ErrorBoundary>
<RiskyCounter />
</ErrorBoundary>
<p>This text is outside the boundary and stays visible.</p>
</div>
);
}Click until the count reaches 3: the counter is replaced by the fallback, while the heading and the text below keep working. "Try again" clears the error and renders the counter again, starting fresh at 0, because the crashed component was removed and its state was lost.
Where to put boundaries#
- One near the top of the app, showing a friendly "Something went wrong" page instead of a blank screen.
- Around independent widgets (a chart, a comments section, a sidebar), so one failing part does not take down the page.
- Around each route or page section in larger apps.
What error boundaries do not catch#
Boundaries catch errors thrown while rendering, in lifecycle methods, and in constructors of the components below them. They do not catch errors in:
- event handlers (use
try/catchinside the handler and set error state); - asynchronous code such as
setTimeoutor afetchcallback (catch the error and store it in state, then render or throw it during the next render); - server-side rendering;
- the error boundary itself (errors there go to the next boundary above).
import { useState } from "react";
export default function App() {
const [error, setError] = useState(null);
function handlePay() {
try {
throw new Error("Payment service unavailable");
} catch (err) {
setError(err.message);
}
}
return (
<div>
<button onClick={handlePay}>Pay ₹499</button>
{error && <p style={{ color: "crimson" }}>Could not pay: {error}</p>}
</div>
);
}react-error-boundary#
Most projects use the small react-error-boundary library instead of writing the class themselves. It provides an ErrorBoundary component with fallbackRender, a reset function, and a useErrorBoundary hook for sending errors from event handlers and async code to the nearest boundary:
import { ErrorBoundary } from "react-error-boundary";
function Fallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>Something went wrong: {error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
<ErrorBoundary FallbackComponent={Fallback}>
<Comments />
</ErrorBoundary>;(This snippet needs npm install react-error-boundary, so it is shown as code rather than as a live preview.)
Common mistakes#
Practice#
Practice exercise
Wrap two "product widgets" in separate error boundaries. One renders normally; the other throws because its product data is missing (product is undefined). Show that the first widget still works.
Show hint
Reuse the ErrorBoundary class. A ProductWidget({ product }) that reads product.name throws a TypeError when product is undefined.
Show solution
import { Component } from "react";
class ErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error };
}
render() {
if (this.state.error) return <p style={{ color: "crimson" }}>Widget unavailable.</p>;
return this.props.children;
}
}
function ProductWidget({ product }) {
return <p>{product.name}: ₹{product.price.toFixed(2)}</p>;
}
export default function App() {
return (
<div>
<ErrorBoundary>
<ProductWidget product={{ name: "Desk lamp", price: 1299 }} />
</ErrorBoundary>
<ErrorBoundary>
<ProductWidget product={undefined} />
</ErrorBoundary>
</div>
);
}Key takeaway#
Next: React with TypeScript.
Finished this lesson?