https://github.com/sunny-117/eslint-plugin-react-boundary
ESLint plugin to ensure React components are wrapped with Boundary
https://github.com/sunny-117/eslint-plugin-react-boundary
boundary error-boundary eslint eslint-plugin eslint-plugin-react eslint-rules react
Last synced: about 2 months ago
JSON representation
ESLint plugin to ensure React components are wrapped with Boundary
- Host: GitHub
- URL: https://github.com/sunny-117/eslint-plugin-react-boundary
- Owner: Sunny-117
- Created: 2025-09-12T08:50:10.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2025-09-12T09:29:37.000Z (10 months ago)
- Last Synced: 2025-09-12T10:37:56.239Z (10 months ago)
- Topics: boundary, error-boundary, eslint, eslint-plugin, eslint-plugin-react, eslint-rules, react
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/eslint-plugin-react-boundary
- Size: 58.6 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# eslint-plugin-react-boundary
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
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 `` 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:
1. **`` wrapper**: Catches rendering errors within component trees
2. **`withBoundary()` HOC**: Provides additional error handling at the component level, including initialization errors
## Installation
```bash
npm install --save-dev eslint-plugin-react-boundary
```
## Configuration
### ESLint 9+ (Flat Config)
```javascript
// 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)
```json
{
"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 `` 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:**
```json
{
"rules": {
"react-boundary/require-boundary": [
"error",
{
"boundaryComponent": "ErrorBoundary",
"importSource": "@/components/ErrorBoundary"
}
]
}
}
```
**Multiple boundary components:**
```json
{
"rules": {
"react-boundary/require-boundary": [
"error",
{
"boundaryComponent": ["Boundary", "ErrorBoundary"],
"importSource": "@/components/ErrorBoundary"
}
]
}
}
```
#### Examples
**❌ Incorrect:**
```jsx
// Missing Boundary wrapper
export function MyComponent() {
return
Hello World;
}
// Separate definition and export without Boundary
function NewOverview() {
return
Overview;
}
export default NewOverview;
```
**✅ Correct:**
```jsx
import { Boundary } from 'react-suspense-boundary';
// Wrapped with Boundary
export function MyComponent() {
return (
Hello World
);
}
// Separate definition with Boundary
function NewOverview() {
return (
Overview
);
}
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 `` 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:**
```json
{
"rules": {
"react-boundary/require-with-boundary": [
"error",
{
"withBoundaryFunction": "withBoundary",
"importSource": "@/components/ErrorBoundary"
}
]
}
}
```
**Advanced Configuration:**
```json
{
"rules": {
"react-boundary/require-with-boundary": [
"error",
{
"withBoundaryFunction": "withBoundary",
"importSource": "react-suspense-boundary",
"boundaryComponent": ["Boundary", "ErrorBoundary"],
"enableHOCDetection": true
}
]
}
}
```
**Disable HOC Detection:**
```json
{
"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):**
```jsx
import React, { forwardRef, memo, lazy } from 'react';
// forwardRef without withBoundary - not allowed
export default forwardRef((props, ref) => {
return
Component;
});
// memo without withBoundary - not allowed
export const MemoComponent = memo(() => {
return
Memoized;
});
// lazy without withBoundary - not allowed
export default lazy(() => import('./Component'));
```
**✅ HOC Examples (corrected):**
```jsx
import React, { forwardRef, memo, lazy } from 'react';
import { withBoundary } from 'react-suspense-boundary';
// forwardRef with withBoundary
export default withBoundary(forwardRef((props, ref) => {
return
Component;
}));
// memo with withBoundary
const MemoComponent = memo(() => {
return
Memoized;
});
export const WrappedMemoComponent = withBoundary(MemoComponent);
// lazy with withBoundary
export default withBoundary(lazy(() => import('./Component')));
```
#### Examples
**❌ Incorrect:**
```jsx
// Direct export - not allowed
export default function NewOverview() {
return
Overview;
}
// Direct named export - not allowed
function MyComponent() {
return
Component;
}
export { MyComponent };
```
**✅ Correct:**
```jsx
import { withBoundary } from 'react-suspense-boundary';
// Wrapped with withBoundary HOC
function NewOverview() {
return
Overview;
}
export default withBoundary(NewOverview);
// Named export with withBoundary
function MyComponent() {
return
Component;
}
const WrappedComponent = withBoundary(MyComponent);
export { WrappedComponent };
```
## Combining Both Rules
For maximum error coverage, use both rules together:
```json
{
"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:**
```jsx
import { Boundary, withBoundary } from '@/components/ErrorBoundary';
// Component with both protections
function MyComponent() {
return (
Protected content
);
}
// Export with HOC wrapper for additional protection
export default withBoundary(MyComponent);
```
## What the Plugin Checks
Both rules automatically detect:
1. **React component files**: Files containing JSX, React imports, or .jsx/.tsx extensions
2. **Function components**: Functions that return JSX elements
3. **Component naming**: Functions with names starting with uppercase letters
4. **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;`
5. **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
```bash
# 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
```
### Project 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 files
```
## Error Handling Coverage
| Error Type | `` | `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.
## License
[MIT](./LICENSE) License © [Sunny-117](https://github.com/Sunny-117)
[npm-version-src]: https://img.shields.io/npm/v/eslint-plugin-react-boundary?style=flat&colorA=080f12&colorB=1fa669
[npm-version-href]: https://npmjs.com/package/eslint-plugin-react-boundary
[npm-downloads-src]: https://img.shields.io/npm/dm/eslint-plugin-react-boundary?style=flat&colorA=080f12&colorB=1fa669
[npm-downloads-href]: https://npmjs.com/package/eslint-plugin-react-boundary
[bundle-src]: https://img.shields.io/bundlephobia/minzip/eslint-plugin-react-boundary?style=flat&colorA=080f12&colorB=1fa669&label=minzip
[bundle-href]: https://bundlephobia.com/result?p=eslint-plugin-react-boundary
[license-src]: https://img.shields.io/github/license/Sunny-117/eslint-plugin-react-boundary.svg?style=flat&colorA=080f12&colorB=1fa669
[license-href]: https://github.com/Sunny-117/eslint-plugin-react-boundary/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
[jsdocs-href]: https://www.jsdocs.io/package/eslint-plugin-react-boundary