🛡️ Error Boundaries: Your App’s Safety Net
The Story of the Brave Firefighter
Imagine your React app is a big, beautiful building full of rooms. Each room is a component. Sometimes, a room catches fire (an error happens). Without protection, the whole building burns down!
Error Boundaries are like firefighters 🚒 stationed at key doors. When fire breaks out in one room, they contain it there. The rest of the building stays safe!
🎯 What You’ll Master
- Error boundary basics - What they are and why we need them
- Class component lifecycle - The special powers only class components have
- componentDidCatch - Catching the fire
- getDerivedStateFromError - Raising the alarm
- Error fallback UI - Showing a friendly “oops” message
1. Error Boundary Basics
What is an Error Boundary?
Think of it like a safety bubble around your components.
// Without Error Boundary:
// One broken component = ENTIRE APP CRASHES! 😱
// With Error Boundary:
// One broken component = Only that part shows
// a friendly error message. App keeps working! ✅
The Simple Truth
An Error Boundary is a special React component that catches JavaScript errors in its child components and displays a fallback UI instead of crashing.
Real-Life Example
<ErrorBoundary>
<UserProfile /> {/* If this crashes... */}
<UserSettings /> {/* ...these still work! */}
</ErrorBoundary>
Key Rule: Error Boundaries catch errors in:
- ✅ Rendering
- ✅ Lifecycle methods
- ✅ Constructors of child components
They DON’T catch errors in:
- ❌ Event handlers (use try-catch)
- ❌ Async code (like setTimeout)
- ❌ Server-side rendering
- ❌ Errors in the boundary itself
2. Class Component Lifecycle
Why Class Components?
Here’s a secret: Error Boundaries MUST be class components. React hooks can’t do this yet!
// ✅ This works - Class component
class ErrorBoundary extends React.Component {
// Special error-catching powers here!
}
// ❌ This CANNOT be an error boundary
function ErrorBoundary() {
// No error-catching powers for functions! 😢
}
The Lifecycle Flow
graph TD A["Component Mounts"] --> B["Child Renders"] B --> C{Error Occurs?} C -->|Yes| D["getDerivedStateFromError"] D --> E["componentDidCatch"] E --> F["Show Fallback UI"] C -->|No| G["App Works Normally"]
Basic Class Structure
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
// Two special methods make this
// an Error Boundary...
}
3. getDerivedStateFromError
The Alarm System 🚨
This method is like your smoke detector. The moment smoke appears, it beeps!
static getDerivedStateFromError(error) {
// The fire is detected!
// Return new state to trigger re-render
return { hasError: true };
}
Key Facts
| Feature | Description |
|---|---|
| Type | Static method |
| When called | During render phase |
| Purpose | Update state |
| Side effects | ❌ Not allowed |
Complete Example
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
// Update state so next render shows
// the fallback UI
return {
hasError: true,
errorMessage: error.message
};
}
render() {
if (this.state.hasError) {
return <h1>Oops! Something broke.</h1>;
}
return this.props.children;
}
}
4. componentDidCatch
The Investigation Team 🔍
After the alarm sounds, investigators arrive. They take notes about what went wrong.
componentDidCatch(error, errorInfo) {
// error: What went wrong
// errorInfo: Where it went wrong
console.log('Error:', error);
console.log('Stack:', errorInfo.componentStack);
// Send to error tracking service
logErrorToService(error, errorInfo);
}
The Two Parameters
componentDidCatch(error, errorInfo) {
// error.message = "Cannot read property..."
// error.name = "TypeError"
// errorInfo.componentStack =
// "in BrokenComponent"
// "in ErrorBoundary"
// "in App"
}
When to Use What?
graph TD A["Error Happens"] --> B["getDerivedStateFromError"] B --> C["Updates state for fallback UI"] A --> D["componentDidCatch"] D --> E["Logs error details"] D --> F["Sends to analytics"]
| Method | Purpose | Side Effects? |
|---|---|---|
getDerivedStateFromError |
Update UI | ❌ No |
componentDidCatch |
Log errors | ✅ Yes |
5. Error Fallback UI
Making Errors Friendly 💝
When things break, users should see something helpful—not a blank screen!
Simple Fallback
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return (
<div className="error-box">
<h2>😅 Oops!</h2>
<p>Something went wrong.</p>
<button onClick={() =>
this.setState({ hasError: false })
}>
Try Again
</button>
</div>
);
}
return this.props.children;
}
}
Custom Fallback Component
// Reusable error boundary with custom UI
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
logToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// Use custom fallback if provided
return this.props.fallback || (
<DefaultErrorUI />
);
}
return this.props.children;
}
}
// Usage:
<ErrorBoundary fallback={<MyErrorPage />}>
<RiskyComponent />
</ErrorBoundary>
Strategic Placement
Place error boundaries at different levels:
<App>
<ErrorBoundary> {/* Whole app safety */}
<Header />
<ErrorBoundary> {/* Main content only */}
<MainContent />
</ErrorBoundary>
<ErrorBoundary> {/* Sidebar only */}
<Sidebar />
</ErrorBoundary>
<Footer />
</ErrorBoundary>
</App>
🎁 Putting It All Together
Here’s a complete, production-ready Error Boundary:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Step 1: Update state for fallback UI
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Step 2: Log the error
this.setState({ errorInfo });
console.error('Caught error:', error);
// Send to your error tracking
// logToService(error, errorInfo);
}
handleReset = () => {
this.setState({
hasError: false,
error: null,
errorInfo: null
});
};
render() {
if (this.state.hasError) {
return (
<div style={{ padding: '20px' }}>
<h2>🛡️ Something went wrong</h2>
<p>{this.state.error?.message}</p>
<button onClick={this.handleReset}>
Try Again
</button>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
🚀 Quick Recap
| Concept | Remember This |
|---|---|
| Error Boundary | Safety bubble for components |
| Class Component | Required! Hooks can’t do this |
| getDerivedStateFromError | Returns new state (the alarm) |
| componentDidCatch | Logs errors (the investigation) |
| Fallback UI | Friendly message when things break |
🎭 The Firefighter’s Motto
“Catch errors at the door, so the whole house doesn’t burn down.”
Error Boundaries are your app’s first responders. Place them wisely, and your users will never see a blank white screen of doom again!
You’ve got this! 🎉
