{"id":31072327,"url":"https://github.com/sunny-117/eslint-plugin-react-boundary","last_synced_at":"2026-05-07T04:39:16.511Z","repository":{"id":314415488,"uuid":"1055426588","full_name":"Sunny-117/eslint-plugin-react-boundary","owner":"Sunny-117","description":"ESLint plugin to ensure React components are wrapped with Boundary","archived":false,"fork":false,"pushed_at":"2025-09-12T09:29:37.000Z","size":60,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-12T10:37:56.239Z","etag":null,"topics":["boundary","error-boundary","eslint","eslint-plugin","eslint-plugin-react","eslint-rules","react"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/eslint-plugin-react-boundary","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sunny-117.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-12T08:50:10.000Z","updated_at":"2025-09-12T09:43:53.000Z","dependencies_parsed_at":"2025-09-12T10:38:01.077Z","dependency_job_id":"89151e15-9e50-4434-a244-13fb2bea26c8","html_url":"https://github.com/Sunny-117/eslint-plugin-react-boundary","commit_stats":null,"previous_names":["sunny-117/eslint-plugin-react-boundary"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Sunny-117/eslint-plugin-react-boundary","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sunny-117%2Feslint-plugin-react-boundary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sunny-117%2Feslint-plugin-react-boundary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sunny-117%2Feslint-plugin-react-boundary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sunny-117%2Feslint-plugin-react-boundary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sunny-117","download_url":"https://codeload.github.com/Sunny-117/eslint-plugin-react-boundary/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sunny-117%2Feslint-plugin-react-boundary/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275344043,"owners_count":25447898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-15T02:00:09.272Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["boundary","error-boundary","eslint","eslint-plugin","eslint-plugin-react","eslint-rules","react"],"created_at":"2025-09-16T00:45:02.257Z","updated_at":"2026-05-07T04:39:16.497Z","avatar_url":"https://github.com/Sunny-117.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eslint-plugin-react-boundary\n\n[![npm version][npm-version-src]][npm-version-href]\n[![npm downloads][npm-downloads-src]][npm-downloads-href]\n[![bundle][bundle-src]][bundle-href]\n[![JSDocs][jsdocs-src]][jsdocs-href]\n[![License][license-src]][license-href]\n\nESLint plugin to ensure React components are properly wrapped with error boundaries for better error handling and application stability.\n\n## Why Error Boundaries Matter\n\nError boundaries are crucial for React applications because they prevent component errors from crashing the entire application. However, traditional `\u003cBoundary\u003e` components have limitations and cannot catch errors in:\n\n- **Event handlers** (these need try/catch blocks)\n- **Asynchronous code** (setTimeout, Promise, async/await, etc.)\n- **Server-side rendering** errors\n- **Component definition stage errors** (errors during component initialization)\n\nThis is why we provide two complementary approaches:\n\n1. **`\u003cBoundary\u003e` wrapper**: Catches rendering errors within component trees\n2. **`withBoundary()` HOC**: Provides additional error handling at the component level, including initialization errors\n\n## Installation\n\n```bash\nnpm install --save-dev eslint-plugin-react-boundary\n```\n\n## Configuration\n\n### ESLint 9+ (Flat Config)\n\n```javascript\n// eslint.config.js\nexport default [\n  {\n    files: ['**/*.{js,jsx,ts,tsx}'],\n    plugins: {\n      'react-boundary': require('eslint-plugin-react-boundary')\n    },\n    rules: {\n      'react-boundary/require-boundary': 'error',\n      'react-boundary/require-with-boundary': 'error'\n    }\n  }\n];\n```\n\n### ESLint 8 and below (Legacy Config)\n\n```json\n{\n  \"plugins\": [\"react-boundary\"],\n  \"rules\": {\n    \"react-boundary/require-boundary\": \"error\",\n    \"react-boundary/require-with-boundary\": \"error\"\n  }\n}\n```\n\n## Rules\n\n### Rule: `require-boundary`\n\nThis rule ensures that all exported React components have their JSX content wrapped with a `\u003cBoundary\u003e` component to catch rendering errors.\n\n#### Options\n\n- `boundaryComponent` (string | string[]): The name(s) of the boundary component(s) to check for. Default: `\"Boundary\"`\n- `importSource` (string): The module from which the boundary component should be imported. Default: `\"react-suspense-boundary\"`\n\n#### Configuration Examples\n\n**Single boundary component:**\n```json\n{\n  \"rules\": {\n    \"react-boundary/require-boundary\": [\n      \"error\",\n      {\n        \"boundaryComponent\": \"ErrorBoundary\",\n        \"importSource\": \"@/components/ErrorBoundary\"\n      }\n    ]\n  }\n}\n```\n\n**Multiple boundary components:**\n```json\n{\n  \"rules\": {\n    \"react-boundary/require-boundary\": [\n      \"error\",\n      {\n        \"boundaryComponent\": [\"Boundary\", \"ErrorBoundary\"],\n        \"importSource\": \"@/components/ErrorBoundary\"\n      }\n    ]\n  }\n}\n```\n\n#### Examples\n\n**❌ Incorrect:**\n```jsx\n// Missing Boundary wrapper\nexport function MyComponent() {\n  return \u003cdiv\u003eHello World\u003c/div\u003e;\n}\n\n// Separate definition and export without Boundary\nfunction NewOverview() {\n  return \u003cdiv\u003eOverview\u003c/div\u003e;\n}\nexport default NewOverview;\n```\n\n**✅ Correct:**\n```jsx\nimport { Boundary } from 'react-suspense-boundary';\n\n// Wrapped with Boundary\nexport function MyComponent() {\n  return (\n    \u003cBoundary\u003e\n      \u003cdiv\u003eHello World\u003c/div\u003e\n    \u003c/Boundary\u003e\n  );\n}\n\n// Separate definition with Boundary\nfunction NewOverview() {\n  return (\n    \u003cBoundary\u003e\n      \u003cdiv\u003eOverview\u003c/div\u003e\n    \u003c/Boundary\u003e\n  );\n}\nexport default NewOverview;\n```\n\n### Rule: `require-with-boundary`\n\nThis 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.\n\n#### Why `withBoundary()` is necessary\n\nWhile `\u003cBoundary\u003e` components catch rendering errors, they cannot catch:\n\n- **Event handler errors**: Errors in onClick, onChange, etc. handlers\n- **Asynchronous errors**: Errors in setTimeout, Promise chains, async/await\n- **Server-side rendering errors**: Errors during SSR\n- **Component definition errors**: Errors during component initialization/construction\n\nThe `withBoundary()` HOC provides additional error handling at the component level to catch these scenarios.\n\n#### Options\n\n- `withBoundaryFunction` (string): The name of the HOC function. Default: `\"withBoundary\"`\n- `importSource` (string): The module from which the HOC should be imported. Default: `\"react-suspense-boundary\"`\n- `boundaryComponent` (string | string[]): Boundary component name(s) for special case detection. Default: `\"Boundary\"`\n- `enableHOCDetection` (boolean): Whether to detect and validate React HOC components (forwardRef, memo, lazy, etc.). Default: `true`\n\n#### Configuration Examples\n\n**Basic Configuration:**\n```json\n{\n  \"rules\": {\n    \"react-boundary/require-with-boundary\": [\n      \"error\",\n      {\n        \"withBoundaryFunction\": \"withBoundary\",\n        \"importSource\": \"@/components/ErrorBoundary\"\n      }\n    ]\n  }\n}\n```\n\n**Advanced Configuration:**\n```json\n{\n  \"rules\": {\n    \"react-boundary/require-with-boundary\": [\n      \"error\",\n      {\n        \"withBoundaryFunction\": \"withBoundary\",\n        \"importSource\": \"react-suspense-boundary\",\n        \"boundaryComponent\": [\"Boundary\", \"ErrorBoundary\"],\n        \"enableHOCDetection\": true\n      }\n    ]\n  }\n}\n```\n\n**Disable HOC Detection:**\n```json\n{\n  \"rules\": {\n    \"react-boundary/require-with-boundary\": [\n      \"error\",\n      {\n        \"withBoundaryFunction\": \"withBoundary\",\n        \"importSource\": \"react-suspense-boundary\",\n        \"enableHOCDetection\": false\n      }\n    ]\n  }\n}\n```\n\n#### HOC Detection\n\nWhen `enableHOCDetection` is `true` (default), the rule will also validate React Higher-Order Components:\n\n- `React.forwardRef()` / `forwardRef()`\n- `React.memo()` / `memo()`\n- `React.lazy()` / `lazy()`\n- Nested HOCs like `memo(forwardRef(...))`\n\n**❌ HOC Examples (when enableHOCDetection: true):**\n```jsx\nimport React, { forwardRef, memo, lazy } from 'react';\n\n// forwardRef without withBoundary - not allowed\nexport default forwardRef((props, ref) =\u003e {\n  return \u003cdiv ref={ref}\u003eComponent\u003c/div\u003e;\n});\n\n// memo without withBoundary - not allowed\nexport const MemoComponent = memo(() =\u003e {\n  return \u003cdiv\u003eMemoized\u003c/div\u003e;\n});\n\n// lazy without withBoundary - not allowed\nexport default lazy(() =\u003e import('./Component'));\n```\n\n**✅ HOC Examples (corrected):**\n```jsx\nimport React, { forwardRef, memo, lazy } from 'react';\nimport { withBoundary } from 'react-suspense-boundary';\n\n// forwardRef with withBoundary\nexport default withBoundary(forwardRef((props, ref) =\u003e {\n  return \u003cdiv ref={ref}\u003eComponent\u003c/div\u003e;\n}));\n\n// memo with withBoundary\nconst MemoComponent = memo(() =\u003e {\n  return \u003cdiv\u003eMemoized\u003c/div\u003e;\n});\nexport const WrappedMemoComponent = withBoundary(MemoComponent);\n\n// lazy with withBoundary\nexport default withBoundary(lazy(() =\u003e import('./Component')));\n```\n\n#### Examples\n\n**❌ Incorrect:**\n```jsx\n// Direct export - not allowed\nexport default function NewOverview() {\n  return \u003cdiv\u003eOverview\u003c/div\u003e;\n}\n\n// Direct named export - not allowed\nfunction MyComponent() {\n  return \u003cdiv\u003eComponent\u003c/div\u003e;\n}\nexport { MyComponent };\n```\n\n**✅ Correct:**\n```jsx\nimport { withBoundary } from 'react-suspense-boundary';\n\n// Wrapped with withBoundary HOC\nfunction NewOverview() {\n  return \u003cdiv\u003eOverview\u003c/div\u003e;\n}\nexport default withBoundary(NewOverview);\n\n// Named export with withBoundary\nfunction MyComponent() {\n  return \u003cdiv\u003eComponent\u003c/div\u003e;\n}\nconst WrappedComponent = withBoundary(MyComponent);\nexport { WrappedComponent };\n```\n\n## Combining Both Rules\n\nFor maximum error coverage, use both rules together:\n\n```json\n{\n  \"rules\": {\n    \"react-boundary/require-boundary\": [\n      \"error\",\n      {\n        \"boundaryComponent\": [\"Boundary\", \"ErrorBoundary\"],\n        \"importSource\": \"@/components/ErrorBoundary\"\n      }\n    ],\n    \"react-boundary/require-with-boundary\": [\n      \"error\",\n      {\n        \"withBoundaryFunction\": \"withBoundary\",\n        \"importSource\": \"@/components/ErrorBoundary\"\n      }\n    ]\n  }\n}\n```\n\n**Example with both approaches:**\n```jsx\nimport { Boundary, withBoundary } from '@/components/ErrorBoundary';\n\n// Component with both protections\nfunction MyComponent() {\n  return (\n    \u003cBoundary\u003e\n      \u003cdiv\u003eProtected content\u003c/div\u003e\n    \u003c/Boundary\u003e\n  );\n}\n\n// Export with HOC wrapper for additional protection\nexport default withBoundary(MyComponent);\n```\n\n## What the Plugin Checks\n\nBoth rules automatically detect:\n\n1. **React component files**: Files containing JSX, React imports, or .jsx/.tsx extensions\n2. **Function components**: Functions that return JSX elements\n3. **Component naming**: Functions with names starting with uppercase letters\n4. **Export patterns**:\n   - Named exports: `export function Component()`, `export const Component = ()`\n   - Default exports: `export default function Component()`, `export default Component`\n   - Separate definition and export: `function Component() {}; export default Component;`\n5. **JSX returns**: Functions returning JSX elements, fragments, or conditional JSX\n\n## Features\n\n- ✅ Detects React component files automatically\n- ✅ Supports function declarations and arrow functions\n- ✅ Handles named and default exports\n- ✅ Supports separate definition and export patterns\n- ✅ Configurable boundary component names (supports arrays)\n- ✅ Configurable import sources\n- ✅ Comprehensive error messages\n- ✅ Two complementary error handling strategies\n\n## Development\n\n### Running Tests\n\n```bash\n# Install dependencies\nnpm install\n\n# Run all tests\nnpm test\n\n# Run specific test suites\nnpm run test-cases          # Core rule tests\nnpm run test-separate       # Separate export tests\nnode test-array-config.js   # Array configuration tests\nnode test-with-boundary.js  # withBoundary rule tests\n```\n\n### Project Structure\n\n```\neslint-plugin-react-boundary/\n├── index.js                    # Main plugin file with both rules\n├── test/\n│   └── test-cases.js          # Core test suite\n├── test-plugin.js             # Integration tests\n├── test-separate-export.js    # Separate export functionality tests\n├── test-array-config.js       # Array configuration tests\n├── test-with-boundary.js      # withBoundary rule tests\n├── playground/                # Test environment\n└── examples/                  # Example files\n```\n\n## Error Handling Coverage\n\n| Error Type | `\u003cBoundary\u003e` | `withBoundary()` | Recommended Solution |\n|------------|--------------|------------------|---------------------|\n| Rendering errors | ✅ | ✅ | Either approach |\n| Event handler errors | ❌ | ✅ | `withBoundary()` + try/catch |\n| Async errors | ❌ | ✅ | `withBoundary()` + proper async handling |\n| SSR errors | ❌ | ✅ | `withBoundary()` |\n| Component init errors | ❌ | ✅ | `withBoundary()` |\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n\n## License\n\n[MIT](./LICENSE) License © [Sunny-117](https://github.com/Sunny-117)\n\n\u003c!-- Badges --\u003e\n\n[npm-version-src]: https://img.shields.io/npm/v/eslint-plugin-react-boundary?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[npm-version-href]: https://npmjs.com/package/eslint-plugin-react-boundary\n[npm-downloads-src]: https://img.shields.io/npm/dm/eslint-plugin-react-boundary?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[npm-downloads-href]: https://npmjs.com/package/eslint-plugin-react-boundary\n[bundle-src]: https://img.shields.io/bundlephobia/minzip/eslint-plugin-react-boundary?style=flat\u0026colorA=080f12\u0026colorB=1fa669\u0026label=minzip\n[bundle-href]: https://bundlephobia.com/result?p=eslint-plugin-react-boundary\n[license-src]: https://img.shields.io/github/license/Sunny-117/eslint-plugin-react-boundary.svg?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[license-href]: https://github.com/Sunny-117/eslint-plugin-react-boundary/blob/main/LICENSE\n[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[jsdocs-href]: https://www.jsdocs.io/package/eslint-plugin-react-boundary\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunny-117%2Feslint-plugin-react-boundary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsunny-117%2Feslint-plugin-react-boundary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunny-117%2Feslint-plugin-react-boundary/lists"}