ESLint plugin to ensure React components are properly wrapped with error boundaries for better error handling and application stability.
Error boundaries are crucial for React applications because they prevent component errors from crashing the entire application. However, traditional <Boundary>
components have limitations and cannot catch errors in:
- Event handlers (these need try/catch blocks)
- Asynchronous code (setTimeout, Promise, async/await, etc.)
- Server-side rendering errors
- Component definition stage errors (errors during component initialization)
This is why we provide two complementary approaches:
<Boundary>
wrapper: Catches rendering errors within component treeswithBoundary()
HOC: Provides additional error handling at the component level, including initialization errors
npm install --save-dev eslint-plugin-react-boundary
// eslint.config.js
export default [
{
files: ['**/*.{js,jsx,ts,tsx}'],
plugins: {
'react-boundary': require('eslint-plugin-react-boundary')
},
rules: {
'react-boundary/require-boundary': 'error',
'react-boundary/require-with-boundary': 'error'
}
}
];
{
"plugins": ["react-boundary"],
"rules": {
"react-boundary/require-boundary": "error",
"react-boundary/require-with-boundary": "error"
}
}
This rule ensures that all exported React components have their JSX content wrapped with a <Boundary>
component to catch rendering errors.
boundaryComponent
(string | string[]): The name(s) of the boundary component(s) to check for. Default:"Boundary"
importSource
(string): The module from which the boundary component should be imported. Default:"react-suspense-boundary"
Single boundary component:
{
"rules": {
"react-boundary/require-boundary": [
"error",
{
"boundaryComponent": "ErrorBoundary",
"importSource": "@/components/ErrorBoundary"
}
]
}
}
Multiple boundary components:
{
"rules": {
"react-boundary/require-boundary": [
"error",
{
"boundaryComponent": ["Boundary", "ErrorBoundary"],
"importSource": "@/components/ErrorBoundary"
}
]
}
}
❌ Incorrect:
// Missing Boundary wrapper
export function MyComponent() {
return <div>Hello World</div>;
}
// Separate definition and export without Boundary
function NewOverview() {
return <div>Overview</div>;
}
export default NewOverview;
✅ Correct:
import { Boundary } from 'react-suspense-boundary';
// Wrapped with Boundary
export function MyComponent() {
return (
<Boundary>
<div>Hello World</div>
</Boundary>
);
}
// Separate definition with Boundary
function NewOverview() {
return (
<Boundary>
<div>Overview</div>
</Boundary>
);
}
export default NewOverview;
This rule ensures that all exported React components are wrapped with a withBoundary()
Higher-Order Component (HOC) to provide comprehensive error handling, including component initialization errors.
While <Boundary>
components catch rendering errors, they cannot catch:
- Event handler errors: Errors in onClick, onChange, etc. handlers
- Asynchronous errors: Errors in setTimeout, Promise chains, async/await
- Server-side rendering errors: Errors during SSR
- Component definition errors: Errors during component initialization/construction
The withBoundary()
HOC provides additional error handling at the component level to catch these scenarios.
withBoundaryFunction
(string): The name of the HOC function. Default:"withBoundary"
importSource
(string): The module from which the HOC should be imported. Default:"react-suspense-boundary"
boundaryComponent
(string | string[]): Boundary component name(s) for special case detection. Default:"Boundary"
enableHOCDetection
(boolean): Whether to detect and validate React HOC components (forwardRef, memo, lazy, etc.). Default:true
Basic Configuration:
{
"rules": {
"react-boundary/require-with-boundary": [
"error",
{
"withBoundaryFunction": "withBoundary",
"importSource": "@/components/ErrorBoundary"
}
]
}
}
Advanced Configuration:
{
"rules": {
"react-boundary/require-with-boundary": [
"error",
{
"withBoundaryFunction": "withBoundary",
"importSource": "react-suspense-boundary",
"boundaryComponent": ["Boundary", "ErrorBoundary"],
"enableHOCDetection": true
}
]
}
}
Disable HOC Detection:
{
"rules": {
"react-boundary/require-with-boundary": [
"error",
{
"withBoundaryFunction": "withBoundary",
"importSource": "react-suspense-boundary",
"enableHOCDetection": false
}
]
}
}
When enableHOCDetection
is true
(default), the rule will also validate React Higher-Order Components:
React.forwardRef()
/forwardRef()
React.memo()
/memo()
React.lazy()
/lazy()
- Nested HOCs like
memo(forwardRef(...))
❌ HOC Examples (when enableHOCDetection: true):
import React, { forwardRef, memo, lazy } from 'react';
// forwardRef without withBoundary - not allowed
export default forwardRef((props, ref) => {
return <div ref={ref}>Component</div>;
});
// memo without withBoundary - not allowed
export const MemoComponent = memo(() => {
return <div>Memoized</div>;
});
// lazy without withBoundary - not allowed
export default lazy(() => import('./Component'));
✅ HOC Examples (corrected):
import React, { forwardRef, memo, lazy } from 'react';
import { withBoundary } from 'react-suspense-boundary';
// forwardRef with withBoundary
export default withBoundary(forwardRef((props, ref) => {
return <div ref={ref}>Component</div>;
}));
// memo with withBoundary
const MemoComponent = memo(() => {
return <div>Memoized</div>;
});
export const WrappedMemoComponent = withBoundary(MemoComponent);
// lazy with withBoundary
export default withBoundary(lazy(() => import('./Component')));
❌ Incorrect:
// Direct export - not allowed
export default function NewOverview() {
return <div>Overview</div>;
}
// Direct named export - not allowed
function MyComponent() {
return <div>Component</div>;
}
export { MyComponent };
✅ Correct:
import { withBoundary } from 'react-suspense-boundary';
// Wrapped with withBoundary HOC
function NewOverview() {
return <div>Overview</div>;
}
export default withBoundary(NewOverview);
// Named export with withBoundary
function MyComponent() {
return <div>Component</div>;
}
const WrappedComponent = withBoundary(MyComponent);
export { WrappedComponent };
For maximum error coverage, use both rules together:
{
"rules": {
"react-boundary/require-boundary": [
"error",
{
"boundaryComponent": ["Boundary", "ErrorBoundary"],
"importSource": "@/components/ErrorBoundary"
}
],
"react-boundary/require-with-boundary": [
"error",
{
"withBoundaryFunction": "withBoundary",
"importSource": "@/components/ErrorBoundary"
}
]
}
}
Example with both approaches:
import { Boundary, withBoundary } from '@/components/ErrorBoundary';
// Component with both protections
function MyComponent() {
return (
<Boundary>
<div>Protected content</div>
</Boundary>
);
}
// Export with HOC wrapper for additional protection
export default withBoundary(MyComponent);
Both rules automatically detect:
- React component files: Files containing JSX, React imports, or .jsx/.tsx extensions
- Function components: Functions that return JSX elements
- Component naming: Functions with names starting with uppercase letters
- Export patterns:
- Named exports:
export function Component()
,export const Component = ()
- Default exports:
export default function Component()
,export default Component
- Separate definition and export:
function Component() {}; export default Component;
- Named exports:
- JSX returns: Functions returning JSX elements, fragments, or conditional JSX
- ✅ Detects React component files automatically
- ✅ Supports function declarations and arrow functions
- ✅ Handles named and default exports
- ✅ Supports separate definition and export patterns
- ✅ Configurable boundary component names (supports arrays)
- ✅ Configurable import sources
- ✅ Comprehensive error messages
- ✅ Two complementary error handling strategies
# Install dependencies
npm install
# Run all tests
npm test
# Run specific test suites
npm run test-cases # Core rule tests
npm run test-separate # Separate export tests
node test-array-config.js # Array configuration tests
node test-with-boundary.js # withBoundary rule tests
eslint-plugin-react-boundary/
├── index.js # Main plugin file with both rules
├── test/
│ └── test-cases.js # Core test suite
├── test-plugin.js # Integration tests
├── test-separate-export.js # Separate export functionality tests
├── test-array-config.js # Array configuration tests
├── test-with-boundary.js # withBoundary rule tests
├── playground/ # Test environment
└── examples/ # Example files
Error Type | <Boundary> |
withBoundary() |
Recommended Solution |
---|---|---|---|
Rendering errors | ✅ | ✅ | Either approach |
Event handler errors | ❌ | ✅ | withBoundary() + try/catch |
Async errors | ❌ | ✅ | withBoundary() + proper async handling |
SSR errors | ❌ | ✅ | withBoundary() |
Component init errors | ❌ | ✅ | withBoundary() |
Contributions are welcome! Please feel free to submit a Pull Request.
MIT