React 19 still ships no built-in hook for creating an error boundary. Why must a boundary be a class component, and how do hooks-only codebases deal with that?
answer
- catching happens outside a render
- no hook call to run at that moment
- one class in the codebase is enough
- react-error-boundary wraps it for you
- React 19 moved reporting to the root
basics
~20 sReact exposes error catching only through the class methods getDerivedStateFromError and componentDidCatch; no hook equivalent exists in React 19. Teams write one small boundary class and reuse it, or adopt the react-error-boundary package, which wraps that class in a hook-friendly API.
solid answer
~50 sThere is no hook that can catch a child's render error, because catching happens while React is unwinding the render of a *different* component — the boundary is not rendering at that moment, so there is no hook call to run. React's only public entry points for that moment are the class methods `static getDerivedStateFromError` and `componentDidCatch`, and React 19 did not change that. In practice this is a small cost: you write one boundary class in the codebase, give it a good fallback and reporting, and never write another. Most teams instead install `react-error-boundary`, whose `<ErrorBoundary>` component accepts `FallbackComponent` or `fallbackRender`, plus `onError`, `onReset` and `resetKeys`, and whose `useErrorBoundary()` hook returns `showBoundary` for pushing handler or async errors into the boundary. React 19 also adds `onCaughtError` and `onUncaughtError` on `createRoot`, so app-wide reporting no longer needs to live in the class at all.
code
jsx · 25 linesimport { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';
function Fallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>{error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
function Panel({ userId, load }) {
const { showBoundary } = useErrorBoundary();
return (
<button onClick={() => load(userId).catch(showBoundary)}>Load</button>
);
}
export function Page({ userId, load }) {
return (
<ErrorBoundary FallbackComponent={Fallback} resetKeys={[userId]}>
<Panel userId={userId} load={load} />
</ErrorBoundary>
);
}go deeper
Know that an error boundary has to be a class in React 19, that you normally copy one small class or install react-error-boundary, and that this is the exception to writing everything as functions.
Explain the mechanism: catching happens while React unwinds a child's render, when the boundary is not rendering, so there is no hook call to run — only methods on a persistent class instance.
Show how you actually set it up: one shared boundary with a decent fallback, reset support, showBoundary for escaped async errors, and root-level onCaughtError/onUncaughtError so reporting is not duplicated per boundary.
Argue the boundary between framework primitive and userland ergonomics — React ships the catch hook-in point, the team standardises fallback shape, reset policy and reporting — and decide whether a dependency or an in-house component is the right cost here.
## Why no hook exists Hooks run while a component renders. Error catching happens at a moment when the boundary is *not* rendering: a descendant threw, React is unwinding its work, and it needs somewhere to hand the error before it decides what to re-render. A hook has no way to receive a call at that point — there is no render of the boundary in progress to hook into. What React exposes for that moment are two class members: - `static getDerivedStateFromError(error)`, called during the retried render, which returns a state patch; - `componentDidCatch(error, errorInfo)`, called after commit, where side effects are allowed. The class instance is the thing React can hold onto and call methods on outside a render. That is the whole reason boundaries remain classes in React 19, and the React team has said a function-component equivalent is wanted but has not shipped one. ## Why this is not the problem it sounds like An interviewer sometimes frames this as an inconsistency in React. It is worth pointing out the practical scale: a codebase needs *one* boundary class, maybe two if the fallback differs by context. Everything else — the fallback UI, the retry button, the reporting call — can be ordinary function components and hooks that the class merely renders. ```jsx class ErrorBoundary extends Component { state = { error: null }; static getDerivedStateFromError(error) { return { error }; } componentDidCatch(error, info) { this.props.onError?.(error, info); } render() { if (this.state.error) { // Fallback is a normal function component with hooks. return <this.props.Fallback error={this.state.error} reset={() => this.setState({ error: null })} />; } return this.props.children; } } ``` The class is a thin adapter around React's only public hook-in point; the interesting code stays functional. ## What react-error-boundary adds The widely used `react-error-boundary` package is that adapter, written once and hardened. Its `<ErrorBoundary>` accepts a fallback in three shapes (`fallback` for a plain element, `FallbackComponent` for a component receiving `error` and `resetErrorBoundary`, `fallbackRender` for a render function), plus `onError` for reporting, `onReset` for clearing whatever caused the failure, and `resetKeys` for automatic recovery when a value changes. It also ships `useErrorBoundary()`, which returns `showBoundary`. That solves the other half of the class problem: errors from event handlers and async callbacks never reach a boundary on their own, and `showBoundary(error)` performs the catch-store-rethrow dance for you from inside a function component. What matters for the interview is not the prop list but the reason a library exists here at all: React gives one low-level primitive, and everything ergonomic around it — fallback shapes, reset semantics, a hook for escaped errors — is userland. ## What React 19 changed nearby React 19 did not add a boundary hook, but it did move *reporting* out of the class. `createRoot` accepts `onCaughtError` (an error a boundary handled) and `onUncaughtError` (nothing caught it; the tree unmounts), each receiving the error and an object carrying `componentStack`. Wiring those means individual boundaries no longer need to duplicate logging, and errors from boundaries you forgot to add still reach your monitoring. So a modern setup looks like: one boundary implementation (yours or the library's) for the *UI* decision, root-level callbacks for the *reporting* decision. ## Framing the legacy angle correctly This is the one place in a React 19 codebase where writing a class is current practice rather than legacy-code archaeology. Be explicit about that distinction in an interview: class components are not how you write components any more, but the error boundary class is a live, supported API — not a `UNSAFE_componentWill*`-era relic you are expected to migrate away from.
- Why can't a hook simply subscribe to errors from its children?Because there is no render of the boundary happening when a child throws — React is unwinding another component's work and needs a target to call immediately. A class instance persists outside render and exposes methods React can invoke; a hook only exists during a render that, at that moment, is not running.
- Does using a class boundary force class patterns on the rest of the tree?No. The boundary is a thin adapter: it holds error state and renders either `children` or a fallback. The fallback itself, the retry control, and everything under the boundary are ordinary function components with hooks. Most codebases have exactly one such class and never touch class syntax again.
- With React 19's onCaughtError, is componentDidCatch redundant?Not quite. The root callback gives you uniform, app-wide reporting with the component stack, which is the bulk of the value. `componentDidCatch` still earns its place when a particular boundary needs local context in the report — which feature, which entity id, which user flow failed — that the root callback has no way to know.
saying these in an interview costs you the question
- Claims React 19 added a useErrorBoundary hook to React itself
- Says try/catch inside a component can replace a boundary
- Thinks a class boundary forces class components below it
- Treats the boundary class as deprecated legacy code
- Believes react-error-boundary catches errors React cannot