← All Articles 🖥️ Frontend Quality Medium By Nishant Kumar Verma Updated

React Error Boundaries: Why AI-Generated Apps Crash on One Error

Without Error Boundaries, a single component error crashes your entire React app. Learn how to add resilience to AI-generated UIs.

FE-001

Quick Answer

A React Error Boundary is a component that catches JavaScript errors in its child component tree and displays a fallback UI instead of crashing the entire app. AI tools like Bolt, Lovable, and Cursor almost never generate Error Boundaries, meaning a single broken component takes down your whole page. Adding one takes less than 20 lines of code.

What Happens Without an Error Boundary?

Without Error Boundaries, a single uncaught error thrown while rendering any React component will unmount your entire component tree - including your navigation, unrelated sections, and any data the user was entering. The user sees a blank white screen with no explanation. No error message, no "try again" button, nothing.

In a production React app, one uncaught rendering error with no error boundary takes down the whole page. Apps built with Bolt, Lovable, Cursor or v0 are especially exposed, because AI tools prioritize functionality over resilience patterns.

Error Boundaries were introduced in React 16 in 2017. Nine years later, they remain one of the most underused features in the entire React ecosystem - and AI code generators have made the problem worse.

How AI-Generated React Code Fails

Here's a realistic scenario. An AI tool builds a dashboard with a user profile card. The component tries to access user.preferences.theme but the server returns null for preferences. Without an Error Boundary:

// ❌ BAD - No Error Boundary
// If this component throws, the ENTIRE app blanks out

function UserCard({ user }) {
  // If user.preferences is null, this throws a TypeError
  const theme = user.preferences.theme;

  return (
    <div className={`card card-${theme}`}>
      <h2>{user.name}</h2>
    </div>
  );
}

// App.jsx - No protection whatsoever
function App() {
  return (
    <div>
      <Navigation />
      <Dashboard />  {/* If anything inside throws → white screen */}
      <Footer />
    </div>
  );
}

One null-check away from working, but instead: full crash. The navigation disappears. The footer disappears. The user has no way to navigate away except to reload and lose their work.

The Fix: Adding a React Error Boundary

Error Boundaries must be class components (React does not yet support them as hooks), but you write them once and reuse everywhere:

// ✅ GOOD - Reusable Error Boundary component
import { Component } from 'react';

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

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

  componentDidCatch(error, info) {
    // Log to your error tracking service
    console.error('Component error:', error, info.componentStack);
    // e.g. Sentry.captureException(error);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="error-fallback">
          <p>Something went wrong. <button onClick={() => this.setState({ hasError: false })}>Try again</button></p>
        </div>
      );
    }
    return this.props.children;
  }
}

// Wrap individual sections - not just the root
function App() {
  return (
    <div>
      <Navigation /> {/* Nav never crashes */}
      <ErrorBoundary fallback={<p>Dashboard failed to load.</p>}>
        <Dashboard />
      </ErrorBoundary>
      <Footer /> {/* Footer never crashes */}
    </div>
  );
}

Now if Dashboard throws, only that section shows a fallback. Navigation and footer stay intact. The user can still navigate.

Where to Place Error Boundaries

Placement strategy matters. One root-level Error Boundary is better than nothing, but fine-grained boundaries give users a much better experience:

Placement What It Protects User Impact on Error
Root (App level) Entire app Shows one fallback for everything - better than white screen
Route level Each page/route Only the current page errors; nav stays
Widget/Section level Individual panels, cards, widgets Only the broken widget errors; rest of page works
Data-fetching components Components that call APIs API errors don't cascade to UI

A broken section that leaves the rest of the page working is far easier for users to recover from than a full-page crash. Granular Error Boundaries directly improve recovery rate.

Modern Alternatives: react-error-boundary Library

Writing class components in a hooks-first codebase feels jarring. The react-error-boundary package provides a polished, hook-friendly wrapper:

// ✅ GOOD - Using react-error-boundary package
import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onError={(error, info) => logErrorToService(error, info)}
      onReset={() => { /* optional: reset state */ }}
    >
      <Dashboard />
    </ErrorBoundary>
  );
}

This package also provides the useErrorBoundary hook, whose showBoundary function lets you send an error from async code to the nearest boundary - something a plain class boundary does not catch on its own.

What Error Boundaries Do Not Catch

Error Boundaries only catch errors during rendering, in lifecycle methods, and in constructors. They do not catch:

  • Errors in event handlers (use try/catch inside the handler)
  • Async code like setTimeout or fetch callbacks
  • Errors in the Error Boundary component itself
  • Server-Side Rendering errors (Next.js has its own error.tsx convention)

For Next.js App Router apps, add an error.tsx file to each route segment: Next.js wraps the segment in a React Error Boundary, so it catches rendering errors from both Server and Client Components in that segment. Add your own boundaries inside a page when one widget failing should not replace the whole segment.

How to Find Missing Error Boundaries in Your App

  1. Search your codebase for getDerivedStateFromError or react-error-boundary imports. If neither exists, you have no Error Boundaries.
  2. Test by throwing manually: Add throw new Error('test') inside a component and see if the whole app blanks. If it does, you need boundaries.
  3. Check your Next.js routes for error.tsx files in each route directory.
  4. Automated scanning: Tools like VibeDoctor's Vibe Check automatically flag a React app (FE-010) when they find no error.tsx, no ErrorBoundary file and no error boundary class with componentDidCatch. It is a project-level check, so it tells you the app has no boundary rather than which routes are unprotected. Free to sign up.
  5. Add error tracking: Once you have Error Boundaries, connect componentDidCatch to Sentry, Datadog, or LogRocket so you're notified when real users hit errors.

Another React mistake in AI-generated code is an effect with no cleanup. See our guide to memory leaks in useEffect.

FAQ

Do I need Error Boundaries in Next.js?

Yes. In the App Router, an error.tsx file is itself a React Error Boundary for its route segment and catches errors from both Server and Client Components there; global-error.tsx covers the root layout. Without either, a rendering error takes down the whole page. Add your own boundaries for widgets that should fail on their own. Lovable does not generate Next.js apps (its projects use React with Vite, or TanStack Start), so there you need your own boundaries.

Why doesn't React support Error Boundaries as hooks?

React's hook model doesn't have a hook equivalent for getDerivedStateFromError or componentDidCatch yet. The React team has discussed this but hasn't shipped a solution. The react-error-boundary library wraps the class component for hook-friendly usage.

Will an Error Boundary catch errors from async fetch calls?

No. Error Boundaries only catch errors thrown during rendering. For async errors (failed API calls etc.), use try/catch in your data-fetching code and set an error state that the component renders. React Query and SWR handle this automatically with their error states. The exception is a rejected promise read with React's use(), which React sends to the nearest Error Boundary.

How many Error Boundaries should a typical app have?

At minimum: one at the app root, and one per major page section (sidebar, main content, modals). A well-structured app might have 5-15 Error Boundaries. More granular = better user experience when things go wrong.

Why do AI tools like Bolt not add Error Boundaries?

AI tools optimize for the happy path - generating code that works when everything is correct. Error handling, resilience patterns, and defensive programming require anticipating failure scenarios, which AI tools generally skip unless you explicitly ask for them.

Diagnose your codebase - free

VibeDoctor checks for FE-001 and 148 other issues across 21 diagnostic areas - security, performance, code quality, and more.

SCAN MY APP →