Why does try...catch not catch errors inside JSX?
Short answer:
try...catchdoes not catch errors inside JSX, because JSX is not executable code at the moment it is written, it is a description of the UI, which React processes asynchronously and outside your call stack.
What happens under the hood
When you write something like:
function App() {
try {
return (
<div>
<UserCard user={user} />
</div>
);
} catch (e) {
console.error('Error:', e);
return <ErrorMessage />;
}
}it seems like try...catch should catch any error inside UserCard.
But React does not work directly: JSX turns into an object describing the element tree,
and rendering and running the components happens later, inside React itself,
already on a different call stack.
Visually
- You call
App(). - React calls your component → gets a tree object:
{ type: "div", props: { children: <UserCard user={user} /> } }- Then React itself calls
UserCard()later (inside its Fiber algorithm). IfUserCard()throws an error, it is thrown inside React, not at the place where yourtry...catchwas.
So:
try {
<UserCard user={user} /> // <-- this is not a function call, just a description
} catch (e) {
// this never gets reached
}catches nothing, because the error occurs later, while React is running, not at the moment JSX is "read".
An analogy
Imagine that JSX is not "execution", but a "blueprint of the interface".
You described what needs to be drawn, but React draws it "in the background".
If an error happens during the "drawing" process,
your try...catch will not find out about it, because the code has long since exited your function.
An example of an error
function UserCard({ user }) {
return <div>{user.name}</div>; // user === undefined → an error
}
function App() {
try {
return <UserCard />; // try will not catch it
} catch (e) {
console.error("Render error!", e);
return <h1>Error!</h1>;
}
}React itself will "catch" the error and propagate it upward - if there is an Error Boundary nearby, it shows a fallback. If there is not, the app "crashes" (a white screen).
What actually catches such errors
1. Error Boundary
A class component with componentDidCatch or getDerivedStateFromError:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error('Error in component:', error);
}
render() {
return this.state.hasError ? <h1>Something went wrong</h1> : this.props.children;
}
}Usage:
<ErrorBoundary>
<UserCard />
</ErrorBoundary>Now the error inside UserCard is caught, and the UI does not crash.
2. try...catch in events or effects
try...catch works if the error is thrown within your synchronous stack,
for example, in onClick or useEffect.
<button
onClick={() => {
try {
throw new Error("Error in the handler");
} catch (e) {
console.error("Caught:", e);
}
}}
>
Click
</button>Here the error is caught, because the code runs directly at the moment of the click.
3. Asynchronous errors (try...catch + async/await)
useEffect(() => {
async function loadData() {
try {
const res = await fetch('/api/data');
const json = await res.json();
setData(json);
} catch (e) {
console.error("Error while loading:", e);
}
}
loadData();
}, []);Everything works as usual here: the error is caught, because it is thrown inside your execution context.
Why you cannot just wrap everything in try...catch
Because React controls the order of component calls itself, and may render components:
- asynchronously (in concurrent mode);
- twice (in Strict Mode);
- with deferred execution.
try...catch simply has no access to those execution stacks.
Summary:
| Scenario | Caught by try...catch? | Why |
|---|---|---|
An error in JSX (<UserCard />) | No | Rendering is controlled by React |
An error in useEffect | Yes | The code runs in your stack |
An error in onClick | Yes | The handler is a regular function |
| An error in async code (await) | Yes (if wrapped) | try...catch works with await |
| An error in a component with no Error Boundary | No | React unmounts the tree |
Conclusion:
try...catchdoes not catch errors inside JSX, because component rendering runs outside your call stack, inside React itself.Such errors need Error Boundaries, which are the "React version of try/catch" at the UI level.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.