
In modern web applications, robustness and reliability are essential for delivering a seamless user experience. React, one of the most popular JavaScript libraries for building user interfaces, encourages modularity and component-based architecture. However, like all JavaScript code, React applications are not immune to runtime errors. When these errors occur, especially during rendering components, it can lead to the entire UI crashing unexpectedly. This is where error boundaries come into play, providing a resilient way to catch and gracefully handle these errors.
What Are Error Boundaries?
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. This functionality is vital for building stable and fault-tolerant React applications.
Error boundaries catch errors during the following lifecycle phases:
- During rendering
- In lifecycle methods
- In constructors of the whole tree below them
They do not catch errors for:
- Event handlers (you must handle these manually)
- Asynchronous code (like setTimeout or fetch callbacks)
- Server-side rendering
- Errors thrown in the error boundary component itself
React introduced error boundaries in version 16, and they have since become a standard method for handling component errors gracefully.
Why Use Error Boundaries?
Without error boundaries, any error in a component causes the entire component tree to unmount, resulting in a blank screen or a broken component rendering. This can significantly hurt the user experience. Implementing error boundaries allows you to:
- Prevent the entire UI from crashing due to a single component’s failure
- Log errors for debugging and analytics
- Display a custom fallback UI to inform users that something went wrong

This makes users more confident in your app and helps developers catch and resolve issues more easily.
How to Create an Error Boundary
Creating an error boundary involves creating a class component that implements either or both of the following lifecycle methods:
- static getDerivedStateFromError(error): Updates the state to display a fallback UI.
- componentDidCatch(error, errorInfo): Logs error information.
Here is a basic example of creating an error boundary component:
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render shows fallback UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Log the error to an error reporting service
console.error("Error caught by ErrorBoundary: ", error, errorInfo);
}
render() {
if (this.state.hasError) {
// Fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
To use this component, simply wrap any component that might throw an error with the ErrorBoundary
component:
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
Best Practices for Using Error Boundaries
When implementing error boundaries in your React applications, keep these best practices in mind:
- Use multiple error boundaries: Instead of one global wrapper, use several localized error boundaries around components that may fail independently. This improves the granularity and reliability of error handling.
- Create reusable fallback UIs: Standardize fallback components across your project to maintain UI consistency and simplify development.
- Integrate with logging tools: Connect your error boundaries to log error details to third-party services like Sentry, LogRocket, or your own monitoring system for better insight and follow-up.
- Test your boundaries: Include test cases that simulate failing components to ensure that the error boundaries react appropriately under real-world conditions.

Advanced Fallback UIs
A default fallback of “Something went wrong” isn’t always enough. For production-ready apps, offering more informative or branded fallback UIs is a smarter choice. For example, you might include:
- Friendly messages and recovery options
- A link to retry or reload the component
- Support contact information
Here’s an example using a more sophisticated fallback component:
function FallbackComponent({ error }) {
return (
<div>
<h2>Oops! Unable to load content.</h2>
<p>Please try again later or contact support.</p>
<p><i>Error details: {error.message}</i></p>
<button onClick={() => window.location.reload()}>Reload</button>
</div>
);
}
This component can be integrated with a modified error boundary that passes the error state:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
render() {
if (this.state.hasError) {
return <FallbackComponent error={this.state.error} />;
}
return this.props.children;
}
}
Error Boundaries and Functional Components
Currently, only class components can be error boundaries. This limitation stems from the fact that React error boundaries rely on specific lifecycle methods not yet available to functional components. However, this might change in future React updates as the framework continues to evolve toward functional programming practices.
That said, you can still wrap functional components inside a class-based error boundary. Where possible, encapsulate reuse of error boundaries using higher-order components or layout wrappers so as not to clutter the JSX deeply.
Error Boundaries vs Try/Catch
In ordinary JavaScript, try/catch
blocks are used to trap errors. However, they don’t work for React component rendering or lifecycle methods. Error boundaries essentially replicate the idea of try/catch
for React’s declarative components. Think of them as error gates that prevent bad code from crashing the rest of the application.
Updating Error Boundaries
An important caveat is that once an error boundary catches an error, it blocks rendering unless it’s reset manually. This can be done by resetting internal state or wrapping it in conditional logic that reacts to props or key changes. Libraries like react-error-boundary provide hooks to simplify this state-reset mechanism.
Using External Libraries
If you want more advanced capabilities out of the box, consider using community-developed libraries like:
- react-error-boundary – Offers useful hooks and utilities such as
useErrorHandler
and customizableFallbackComponent
. - errboundary – A lightweight error handling library for React projects, with simple APIs and minimal setup.
These tools are especially useful when you need more than just a static class, such as error tracking, error recovery, and user feedback collection.
Conclusion
Error boundaries are an essential feature in React, dramatically improving your application’s ability to cope with unexpected failures. By catching errors in the render tree and presenting alternative UIs, you elevate reliability and trust in your software product. Whether you build your own error boundaries or use libraries, ensuring that your UI doesn’t abruptly break is a worthwhile investment.
As your application grows in complexity, thoughtful implementation of error boundaries becomes increasingly important. Adopt a layered and modular approach, and you’ll protect your users from disruptive bugs while giving your development team the tools they need to maintain high-quality code.