eslint-plugin-react-boundary
v0.0.12
Published
ESLint plugin to ensure React components are wrapped with Boundary
Maintainers
Readme
eslint-plugin-react-boundary
ESLint plugin to ensure React components are properly wrapped with error boundaries for better error handling and application stability.
Why Error Boundaries Matter
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
Installation
npm install --save-dev eslint-plugin-react-boundaryConfiguration
ESLint 9+ (Flat Config)
// 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'
}
}
];ESLint 8 and below (Legacy Config)
{
"plugins": ["react-boundary"],
"rules": {
"react-boundary/require-boundary": "error",
"react-boundary/require-with-boundary": "error"
}
}Rules
Rule: require-boundary
This rule ensures that all exported React components have their JSX content wrapped with a <Boundary> component to catch rendering errors.
Options
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"
Configuration Examples
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"
}
]
}
}Examples
❌ 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;Rule: require-with-boundary
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.
Why withBoundary() is necessary
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.
Options
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
Configuration Examples
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
}
]
}
}HOC Detection
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')));Examples
❌ 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 };Combining Both Rules
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);What the Plugin Checks
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
Features
- ✅ 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
Development
Running Tests
# 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 testsProject Structure
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 filesError Handling Coverage
| 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() |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
