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
componentDidCatchfor 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 .