DEV Community

Cover image for React Mastery Series – Day 29: React Error Handling – Error Boundaries, Logging & Resilient UI Design
Siva Samanthapudi
Siva Samanthapudi

Posted on

React Mastery Series – Day 29: React Error Handling – Error Boundaries, Logging & Resilient UI Design

Welcome back to the React Mastery Series!

In the previous article, we explored React Design Patterns and learned how enterprise applications use:

  • Composition
  • Compound Components
  • Custom Hooks
  • Higher-Order Components (HOCs)
  • Provider Pattern
  • Feature-Based Architecture

Today, we'll learn how to build React applications that can gracefully recover from unexpected failures.

React Error Handling

No application is perfect.

Servers fail.

Networks disconnect.

APIs return unexpected responses.

Developers accidentally introduce bugs.

The goal isn't to eliminate every error—it's to ensure users continue to have a good experience when errors occur.


Why Error Handling Matters

Imagine a customer opening an internet banking application.

           Dashboard
               ↓
        Account Summary
               ↓
          Transactions
               ↓
          Investments
               ↓
      One Component Crashes
               ↓
Entire Application Becomes Blank
Enter fullscreen mode Exit fullscreen mode

This creates a poor user experience.

Instead, we'd rather display:

             Dashboard
                ↓
          Account Summary
                ↓
           Transactions
                ↓
      Investment Widget Crashes
                ↓
     Show Friendly Error Message
                ↓
Rest of Dashboard Continues Working
Enter fullscreen mode Exit fullscreen mode

This approach makes applications more resilient.


Types of Errors in React

React applications commonly encounter four categories of errors.

Error Type Example
Rendering Errors Component crashes while rendering
JavaScript Errors Undefined variables, null references
Network Errors API request failures
Runtime Errors Unexpected user interactions or invalid data

Each category requires a different handling strategy.


What Happens When a Component Crashes?

Consider this component:

function UserProfile() {
  const user = null;

  return <h2>{user.name}</h2>;
}
Enter fullscreen mode Exit fullscreen mode

When rendered:

Cannot read properties of null
Enter fullscreen mode Exit fullscreen mode

Without protection, React may unmount the affected part of the application.


Introducing Error Boundaries

An Error Boundary catches rendering errors in its child component tree.

      Application
           │
           ▼
    Error Boundary
           │
           ▼
       Dashboard
           │
           ▼
Profile Component Crashes
           │
           ▼
   Fallback UI Appears
Enter fullscreen mode Exit fullscreen mode

Instead of a blank page, users see a meaningful message.


Creating an Error Boundary

Error Boundaries are currently implemented using class components.

import React from "react";

type ErrorBoundaryState = {
  hasError: boolean;
};

class ErrorBoundary extends React.Component<
  React.PropsWithChildren,
  ErrorBoundaryState
> {
  state: ErrorBoundaryState = {
    hasError: false,
  };

  static getDerivedStateFromError() {
    return {
      hasError: true,
    };
  }

  componentDidCatch(
    error: Error,
    errorInfo: React.ErrorInfo
  ) {
    console.error(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h2>Something went wrong.</h2>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;
Enter fullscreen mode Exit fullscreen mode

Although modern React primarily uses function components, Error Boundaries still rely on class components.


Using an Error Boundary

Wrap components that could fail.

<ErrorBoundary>
  <Dashboard />
</ErrorBoundary>
Enter fullscreen mode Exit fullscreen mode

Or wrap only critical sections.

<Dashboard>
  <ErrorBoundary>
    <InvestmentWidget />
  </ErrorBoundary>

  <TransactionList />
</Dashboard>
Enter fullscreen mode Exit fullscreen mode

If the investment widget crashes, the transaction list continues working.


What Error Boundaries Catch

✅ Rendering errors
✅ Constructor errors
✅ Lifecycle method errors

They help prevent the application from crashing completely.


What Error Boundaries Don't Catch

Error Boundaries do not catch:

  • Event handler errors
  • Async errors
  • API failures
  • setTimeout() errors
  • Server-side rendering errors

These require different handling strategies.


Handling API Errors

Always anticipate API failures.

async function fetchUsers() {
  try {
    const response = await api.get("/users");

    return response.data;
  } catch (error) {
    console.error(error);

    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

In the UI:

if (error) {
  return (
    <ErrorMessage
      message="Unable to load users."
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Show users a helpful message instead of exposing technical details.


Handling Async Errors

Async functions should always use try...catch.

async function handleSubmit() {
  try {
    await saveProfile();
  } catch (error) {
    setError(
      "Unable to save your profile."
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Gracefully handling failures improves the user experience.


Defensive Rendering

Avoid assuming data always exists.

Instead of:

<h2>{user.name}</h2>
Enter fullscreen mode Exit fullscreen mode

Use optional chaining.

<h2>{user?.name}</h2>
Enter fullscreen mode Exit fullscreen mode

Or provide a fallback.

<h2>{user?.name ?? "Guest User"}</h2>
Enter fullscreen mode Exit fullscreen mode

This prevents many runtime errors.


Global Axios Error Handling

Centralize API error handling with interceptors.

api.interceptors.response.use(
  (response) => response,

  (error) => {
    if (error.response?.status === 401) {
      // Redirect to login
    }

    if (error.response?.status === 500) {
      console.error("Server error");
    }

    return Promise.reject(error);
  }
);
Enter fullscreen mode Exit fullscreen mode

This avoids duplicating error handling across the application.


Logging Errors

Errors should be logged for developers.

Typical information includes:

  • Error message
  • Stack trace
  • Browser
  • User ID (if available)
  • URL
  • Timestamp

Example:

      Error
        ↓
    Log Service
        ↓
Developer Dashboard
        ↓
     Fix Bug
Enter fullscreen mode Exit fullscreen mode

Logging helps identify issues that users encounter in production.


Monitoring with Sentry

Many enterprise applications use monitoring platforms such as Sentry to collect production errors.

Example setup:

import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "YOUR_DSN",
});
Enter fullscreen mode Exit fullscreen mode

These tools automatically collect error details and help teams prioritize fixes.


Building a Friendly Fallback UI

Avoid showing messages like:

TypeError:
Cannot read properties of undefined
Enter fullscreen mode Exit fullscreen mode

Instead, display:

Oops!

Something went wrong.

Please refresh the page or try again later.
Enter fullscreen mode Exit fullscreen mode

Users don't need technical stack traces.

They need clear guidance.


Retry Pattern

Network issues are often temporary.

Instead of:

API Failed
   ↓
  Stop
Enter fullscreen mode Exit fullscreen mode

Prefer:

  API Failed
      ↓
Show Retry Button
      ↓
 Retry Request
      ↓
   Success
Enter fullscreen mode Exit fullscreen mode

This improves resilience for intermittent failures.


Real-World Banking Example

Imagine the transaction history service is temporarily unavailable.

Instead of crashing the dashboard:

Dashboard
   ↓
Accounts ✓

Cards ✓

Loans ✓

Transactions ✗
     ↓
Unable to load transactions.

[ Retry ]
Enter fullscreen mode Exit fullscreen mode

Users can still access the rest of the application.


Folder Structure

A scalable error handling structure:

src

├── components
│   ├── ErrorBoundary.tsx
│   ├── ErrorMessage.tsx
│   └── RetryButton.tsx
├── services
│   └── api.ts
├── hooks
    └── useErrorHandler.ts
Enter fullscreen mode Exit fullscreen mode

Keep error handling reusable and centralized.


Common Mistakes

1. Ignoring API Failures

Every API call can fail.

Always handle:

  • Loading
  • Success
  • Error

2. Showing Technical Errors to Users

Avoid exposing stack traces or exception messages.

Display user-friendly messages instead.


3. Wrapping the Entire App with One Error Boundary

A single Error Boundary can cause the entire application to display fallback UI.

Use multiple boundaries around critical sections when appropriate.


4. Not Logging Errors

If errors aren't logged, developers may never know users experienced them.

Always collect enough information to investigate production issues.


Best Practices

  • Use Error Boundaries around major UI sections.
  • Handle async operations with try...catch.
  • Use optional chaining when accessing nested data.
  • Centralize API error handling.
  • Display meaningful fallback messages.
  • Log errors to a monitoring service.
  • Allow users to retry failed operations whenever possible.

Key Takeaways

Today, we learned:

✅ Error Boundaries prevent rendering errors from crashing the entire application.
✅ Async errors require try...catch.
✅ API failures should display helpful messages and retry options.
✅ Centralized error handling reduces duplicate code.
✅ Logging production errors helps teams identify and fix issues quickly.
✅ Resilient applications continue functioning even when individual components fail.


Coming Next 🚀

In Day 30, we will explore one of the most requested topics by React developers:

React Project Folder Structure & Scalable Architecture

We will learn:

  • Small vs Large Project Structure
  • Feature-Based Architecture
  • Shared Components
  • Services Layer
  • Hooks Organization
  • State Management Structure
  • Environment Configuration
  • Enterprise Folder Organization

By the end of the next article, you'll know how to organize React applications the way large engineering teams at companies like banks, fintech firms, and SaaS organizations structure their production codebases.

Happy Coding! 🚀

Top comments (0)