React Error Boundaries: Why AI-Generated Apps Crash on One Error - VibeDoctor 
← All Articles 🖥️ Frontend Quality Medium

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 JavaScript error in 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.

According to Sentry's 2024 Developer Survey, React rendering errors are the #1 source of complete page crashes in production React applications. The same survey found that 62% of React applications in production have no Error Boundary configured at the root level. For vibe-coded apps built with Bolt, Lovable, Cursor, or v0, the number is even higher - AI tools prioritize functionality over resilience patterns.

Error Boundaries were introduced in React 16 in 2017. Eight 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

Google's UX research shows that users are 4x more likely to recover from a partial page error (one section broken) vs 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, which lets you trigger the boundary from async code - something the native class component API can't do.

What Error Boundaries Do Not Catch

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

For Next.js apps (the most common framework for Bolt and Lovable projects), use both a React Error Boundary for client components and a error.tsx file per route segment for server-side errors.

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 (vibedoctor.io) automatically detect React apps without Error Boundary components (FE-001) and flag exactly which routes and component trees 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.

FAQ

Do I need Error Boundaries in Next.js?

Yes. Next.js has error.tsx for server-side errors per route, but client components still need React Error Boundaries. AI-generated Next.js apps from Bolt or Lovable typically have neither.

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 synchronous rendering errors. 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.

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.

Scan your codebase for this issue - free

VibeDoctor checks for FE-001 and 128 other issues across 15 diagnostic areas.

SCAN MY APP →
← Back to all articles View all 129+ checks →