Featured image of post Robust Frontend Exception Management with Error Boundaries Featured image of post Robust Frontend Exception Management with Error Boundaries

Robust Frontend Exception Management with Error Boundaries

Implement fallback render states on runtime failures and stream crash details cleanly to logging webhooks.

Why Error Boundaries Matter

In React applications, a single uncaught JavaScript error in a component can crash the entire UI. Before Error Boundaries, this meant users would see a white screen with no indication of what went wrong. Error Boundaries are React components that catch JavaScript errors anywhere in their child component tree and render a fallback UI instead of crashing.

Implementing an Error Boundary

Error Boundaries are class components that implement one or both of the static lifecycle methods getDerivedStateFromError and componentDidCatch:

import React, { Component, ErrorInfo, ReactNode } from "react";

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

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

  componentDidCatch(error: Error, info: ErrorInfo) {
    console.error("Error caught:", error, info.componentStack);
    // Send to logging service
    logErrorToService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || <DefaultFallback error={this.state.error} />;
    }
    return this.props.children;
  }
}

Fallback UI Design

A good fallback UI provides recovery options and reduces user frustration:

function DefaultFallback({ error }: { error: Error | null }) {
  return (
    <div role="alert" className="error-boundary-fallback">
      <h2>Something went wrong</h2>
      <p>We encountered an unexpected error. Please try refreshing the page.</p>
      <button onClick={() => window.location.reload()}>
        Refresh Page
      </button>
      {process.env.NODE_ENV === "development" && (
        <pre>{error?.message}</pre>
      )}
    </div>
  );
}

Integrating with Logging Services (Sentry)

Error Boundaries integrate naturally with error monitoring tools like Sentry:

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

class SentryErrorBoundary extends Component<Props, State> {
  componentDidCatch(error: Error, info: ErrorInfo) {
    Sentry.withScope((scope) => {
      scope.setExtras({ componentStack: info.componentStack });
      Sentry.captureException(error);
    });
  }
}

Sentry also provides a built-in Error Boundary wrapper:

<Sentry.ErrorBoundary fallback={<ErrorFallback />}>
  <App />
</Sentry.ErrorBoundary>

Boundary Placement Strategies

Strategic placement of Error Boundaries is crucial:

App
├── ErrorBoundary (global fallback)
│   ├── Layout
│   │   ├── Header
│   │   ├── ErrorBoundary (content area fallback)
│   │   │   ├── MainContent
│   │   │   └── DataTable
│   │   └── Footer
│   └── ErrorBoundary (sidebar fallback)
│       └── SidebarWidget

Guidelines:

  • Wrap each major section (sidebar, main content, modals) independently
  • Use a root boundary only as a last resort
  • Place boundaries at feature boundaries where failure is recoverable
  • Allow healthy parts of the UI to remain functional

Error Recovery Patterns

Beyond showing fallbacks, Error Boundaries can offer recovery:

class RecoverableBoundary extends Component {
  state = { hasError: false, error: null };

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

  handleRetry = () => {
    this.setState({ hasError: false, error: null });
  };

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <p>Error: {this.state.error?.message}</p>
          <button onClick={this.handleRetry}>Retry</button>
        </div>
      );
    }
    return this.props.children;
  }
}

Limitations

Error Boundaries have important limitations to understand:

LimitationExplanation
Event handlersErrors in onClick etc. are NOT caught — use try-catch
Async codesetTimeout or Promise errors are NOT caught
SSRError Boundaries do NOT catch server-side errors
Own errorsAn Error Boundary cannot catch its own errors
State mutationsNot designed to handle corrupted global state

Conclusion

Error Boundaries are an essential part of production React applications. They prevent complete UI crashes, provide graceful degradation, and integrate seamlessly with logging services. By combining strategic boundary placement with proper error logging and recovery patterns, you can build applications that handle failures gracefully and provide actionable diagnostics for developers.