Error boundaries cheatsheet.

Why

A render-time error in any child crashes the whole tree. Error boundaries catch the error and show a fallback UI.

Class boundary (canonical)

class ErrorBoundary extends React.Component<
  { children: React.ReactNode; fallback: (e: Error) => React.ReactNode },
  { error: Error | null }
> {
  state = { error: null as Error | null };
  
  static getDerivedStateFromError(error: Error) {
    return { error };
  }
  
  componentDidCatch(error: Error, info: React.ErrorInfo) {
    logErrorToService(error, info);
  }
  
  render() {
    if (this.state.error) return this.props.fallback(this.state.error);
    return this.props.children;
  }
}

<ErrorBoundary fallback={(e) => <p>error: {e.message}</p>}>
  <App />
</ErrorBoundary>

react-error-boundary

npm i react-error-boundary
import { ErrorBoundary } from "react-error-boundary";

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

<ErrorBoundary
  FallbackComponent={Fallback}
  onError={(err, info) => logError(err, info)}
  onReset={() => resetState()}
>
  <App />
</ErrorBoundary>

Granular boundaries

Don’t wrap one boundary around everything — wrap distinct regions:

<Layout>
  <ErrorBoundary fallback={<HeaderError />}>
    <Header />
  </ErrorBoundary>
  
  <ErrorBoundary fallback={<MainError />}>
    <Main />
  </ErrorBoundary>
  
  <ErrorBoundary fallback={<SidebarError />}>
    <Sidebar />
  </ErrorBoundary>
</Layout>

If Sidebar errors, the rest of the page still works.

With Suspense

<ErrorBoundary FallbackComponent={Fallback}>
  <Suspense fallback={<Loader />}>
    <SlowAsync />
  </Suspense>
</ErrorBoundary>

Suspense handles loading; ErrorBoundary handles errors thrown from suspended children.

What boundaries DON’T catch

  • Event handlers (onClick, etc) — try/catch yourself.
  • Async code outside render — try/catch yourself.
  • SSR errors (depends on framework).
  • Errors in the boundary itself.
function Button() {
  async function onClick() {
    try {
      await save();
    } catch (e) {
      toast.error(e.message);     // not caught by boundary
    }
  }
  return <button onClick={onClick}>Save</button>;
}

Throwing in render to escalate

Convert async errors into render errors so boundaries catch them:

const [error, setError] = useState<Error | null>(null);
if (error) throw error;

async function onClick() {
  try { await save(); }
  catch (e: any) { setError(e); }
}

react-error-boundary’s useErrorBoundary hook does this:

import { useErrorBoundary } from "react-error-boundary";

const { showBoundary } = useErrorBoundary();

async function onClick() {
  try { await save(); }
  catch (e) { showBoundary(e); }
}

Reset on prop change

<ErrorBoundary
  FallbackComponent={Fallback}
  resetKeys={[userId]}     // resets boundary when userId changes
>
  <UserPage id={userId} />
</ErrorBoundary>

Logging

componentDidCatch(error, info) {
  Sentry.captureException(error, { extra: info });
}

Or onError in react-error-boundary.

Next.js error.tsx

// app/error.tsx
"use client";

export default function Error({ error, reset }: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Error: {error.message}</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

Boundary for the whole route segment. Add to nested routes:

app/
├── error.tsx           # global
├── users/
│   └── error.tsx       # users only

Custom error types

class AuthError extends Error {}
class NotFoundError extends Error {}

function Fallback({ error }) {
  if (error instanceof AuthError) return <Login />;
  if (error instanceof NotFoundError) return <NotFound />;
  return <p>Something went wrong</p>;
}

Common mistakes

  • One boundary at root — single error kills everything.
  • Catching event-handler errors in boundary — they don’t reach it.
  • Forgetting componentDidCatch for logging — silent failures in prod.
  • Not resetting boundary — once errored, stuck until prop change.
  • Using boundary as flow control — they’re for unrecoverable errors.

Read this next

If you want my error-boundary + logging stack, it’s at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .