If you have built a React app at any point in your programming career, you probably have experienced an error at some point that was cryptic and did not provide any meaningful context on what actually happened.
This probably means the error occurred at some point in the application and our React component did not handle the error gracefully, well mostly because it was none of its business to make sure the entire app holds.
A common problem in React JS is that if a part of a component breaks, the whole application is broken. A JavaScript error in a part of the UI shouldn’t break the whole app. To solve this problem for React users, React 16 introduces a new concept of an “error boundary”.
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. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
Error screens
Before we dig deeper let’s have a look at some error screens of some famous applications. These are the error screens(fallback UI) of some applications, which will be displayed when something goes wrong in their application.

Developers spend 50% of their time finding errors and fixing them. And errors in your application will naturally lead to unhappy customers, which in turn leads to unhappy management and which in turn makes the developer unhappy. So we need a better approach to find errors, track them and solve them. If we can’t solve them, show some fallback UI which will not disrupt the user experience.
What is the problem in React till now?
- White Screen of death ⚰️(Error in one small component renders a blank page)
- Whole React component tree is crashed 🌲.
- Very difficult to track the issue and debug the code in production 😥
Traditional Ways of capturing errors
1.window.onerror
A window has a callback function called onerror, which will get called if something goes wrong in the browser’s window.

Cons:
- Not supported by all the browsers
- Does not capture Cryptic “Script error”
“Script error”: Happens in Firefox, Safari, and Chrome when an exception violates the browser’s same-origin policy – i.e. when the error occurs in a script that’s hosted on a domain other than the domain of the current page.
- try-catch
Enclose the part of the code in try-catch block.

Cons:
- It only works with imperative code
- Doesn’t capture error due to the state change in react component
- Promise-Catch
Every guarantee Ought to Be followed closely by a grab to Allow You to handle the exceptions.
- ComponentDidCatch
ComponentDidCatch became a part of this React 16 lifecycles. It means that once an error happens on a child’s part, the parent could catch & handle it gracefully. The good news is that by componentDidCatch, you can get the gorgeous Error Boundaries. But the bad news is that it just can capture the error which is going on in the respond lifecycles(constructor, componentDidCatch, render). Here is a good example. If the child component has a button and then pressing on the button throws an exception, that exception will not be captured inside its parent element. You don’t have to mention that this consists of the events like setTimeout & setInterval or some other occasions.
- International Error Handling
For other cases, the above methods do not work correctly; there is another way in which we’re able to utilize the global handler to catch the uncaught exceptions.
6.Others
There are a lot of other methods to capture logs like below
- react-transform-catch-errors
- react-guard
- react-component-errors
- react-safe-render
React 16 (Super Dad)
I call React 16 as Super Dad because what it does to child components.

componentDidCatch() a new life cycle method does the above same thing to its child component.
The componentDidCatch() method works like a JavaScript catch {} block, but for components. Only class components can be error boundaries. In practice, most of the time you’ll want to declare an error boundary component once and use it throughout your application.
A class component becomes an error boundary if it defines a new life cycle method called componentDidCatch(error, info)
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
Then you can use it as a regular component:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
Where to use ErrorBoundary ?
- Root Component (must)
- Main Sections of the page like Header, Footer and Middle Content
- Stand-alone components like Chat Window, Search Bar
- Wherever you suspect that the error might occur
The granularity of error boundaries is up to you. You can wrap top-level route components, you might also wrap components that might contain obscure code. It’s up to you how to best handle protecting against application crashes.
Note that error boundaries only catch errors in the components below them in the tree. An error boundary can’t catch an error within itself. If an error boundary fails when trying to render the error message, the error will propagate to the closest error boundary above it. This, too, is similar to how catch {} block works in JavaScript.
Let me show you some examples of how error boundary works

In the above example, there is one <Parent />component which has a <Child />. <Child/> has three sub childrens <Child1/>,<Child2/> and <Child3/>.
<Child3> is throwing some error, which will be thrown to its successive parent <Child>, as the <Child/> does not implement componentDidCatch() it will be thrown to its successive parent which is <Parent/>

Output:

And console logs this

If the componentDidCatch() would have implemented in the <Child/> it would have been more granular

Output:

When does error boundary catch errors?
- When there is an error in the render
- In lifecycle methods
- In Constructor
- The whole tree below them
When will it won’t catch?
- Errors in the same component
- On event handlers, unless the error is due to a state change
- Error in try catch block unless an error is due to state change
- Error in asynchronous code unless the error is due to state change
- Server-side rendering
Capturing errors
Error monitoring and crash reporting is a vital part of building a high-quality customer centric application.
A release isn’t the end… Errors are your first chance to iterate.
There are various tools for capturing errors
- Sentry
- Bugsnag
- Firebase
- Visual Studio App Center
One of the most popular crash reporting tools for mobile applications is Firebase Crashlytics. But Crashlytics cannot be used in React Native apps because it won’t capture javascript errors. There are other very good crash reporting tools for React Native like Sentry, Bugsnag etc.
One of the most important features of Bugsnag which made the developers life easy was tagging users who got crashes. When a user reports a crash in our app, we can easily search their email id and search for crashes for that particular users.
Conclusion
Building an error-free application is a vital part of product development. React 16 helps us in making React web applications and React Native apps to capture errors and give enough insights to rectify those errors. Especially, here at Groww we use React 16 and using some advanced tools we manage to avoid most of the errors and give a better user experience to our end users.