error-boundaries
Validates usage of Error Boundaries instead of try/catch for errors in child components.
Full Name in eslint-plugin-react-x
react-x/error-boundariesFull Name in @eslint-react/eslint-plugin
@eslint-react/error-boundariesPresets
x
recommended
recommended-typescript
recommended-type-checked
strict
strict-typescript
strict-type-checked
Rule Details
Try/catch blocks can't catch errors that happen during React's rendering process. Errors thrown in rendering methods or lifecycle hooks bubble up through the component tree. Only Error Boundaries can catch these errors.
Similarly, the use hook doesn't throw errors in the traditional sense — it suspends component execution. When use encounters a rejected promise, only Error Boundaries (and Suspense boundaries) can handle these cases. A catch block around use would never run.
Examples
Wrapping JSX in try/catch
React's rendering is declarative. When you write return <ChildComponent />, you're describing what the UI should look like — you're not imperatively executing ChildComponent. If a child throws during rendering, React handles that error propagation internally through the component tree, bypassing any try/catch in JavaScript's call stack.
// Problem: try/catch cannot catch rendering errors in child components
function Parent() {
try {
return <ChildComponent />;
// ^^^ Use an Error Boundary to catch errors in child components.
} catch (error) {
return <div>Error occurred</div>;
}
}// Recommended: Use an Error Boundary to catch rendering errors
function Parent() {
return (
<ErrorBoundary fallback={<div>Error occurred</div>}>
<ChildComponent />
</ErrorBoundary>
);
}Using try/catch with the use hook
The use hook doesn't throw errors in the traditional sense — it suspends component execution. When use encounters a pending promise, it suspends the component and lets React show a fallback. When the promise rejects, React propagates the error through the component tree to the nearest Error Boundary.
// Problem: The catch block will never execute
function Component({ promise }) {
try {
const data = use(promise);
// ^^^ Use an Error Boundary instead of try/catch around the 'use' hook.
return <div>{data}</div>;
} catch (error) {
return <div>Failed to load</div>;
}
}// Recommended: Use an Error Boundary with Suspense to handle async data
function App() {
return (
<ErrorBoundary fallback={<div>Failed to load</div>}>
<Suspense fallback={<div>Loading...</div>}>
<DataComponent promise={fetchData()} />
</Suspense>
</ErrorBoundary>
);
}Non-rendering operations
Try/catch is perfectly valid for synchronous operations like JSON.parse, localStorage access, or data transformations that happen before JSX is returned. It is also valid inside event handlers and effects. The rule only flags try/catch blocks that wrap JSX return statements or use hook calls inside component functions.
// OK: Synchronous data processing
function Component() {
let data;
try {
data = JSON.parse(text);
} catch (e) {
data = null;
}
return <div>{data}</div>;
}// OK: Error handling in event handlers
function Component() {
const handleClick = () => {
try {
doSomething();
} catch (e) {
console.error(e);
}
};
return <button onClick={handleClick}>Click</button>;
}Troubleshooting
Why is the linter telling me not to wrap use in try/catch?
The use hook doesn't throw errors in the traditional sense — it suspends component execution. When use encounters a pending promise, it suspends the component and lets React show a fallback. Only Suspense and Error Boundaries can handle these cases. The linter warns against try/catch around use to prevent confusion as the catch block would never run.
// Problem: try/catch around `use` hook
function Component({ promise }) {
try {
const data = use(promise); // Won't catch — `use` suspends, not throws
return <div>{data}</div>;
} catch (error) {
return <div>Failed to load</div>; // Unreachable
}
}// Recommended: Use an Error Boundary with Suspense
function App() {
return (
<ErrorBoundary fallback={<div>Failed to load</div>}>
<Suspense fallback={<div>Loading...</div>}>
<DataComponent promise={fetchData()} />
</Suspense>
</ErrorBoundary>
);
}Versions
Resources
Further Reading
See Also
react-x/no-class-component
Disallows class components except for error boundaries.react-x/rules-of-hooks
Enforces the Rules of Hooks.