Thank you for your interest in contributing to ESLint React! Whether you're fixing a bug, proposing a new rule, improving documentation, or helping with tooling, every contribution is welcome.
Note
ESLint React is not a fork of or derived from eslint-plugin-react. Features in eslint-plugin-react may not appear in ESLint React.
- Prerequisites
- Getting Started
- Monorepo Structure
- Development Commands
- Reporting Issues
- Submitting Pull Requests
- Developing a New Rule
- Testing
- Code Style
- Code of Conduct
Before you begin, make sure you have the following installed:
| Requirement | Version |
|---|---|
| Node.js | ≥ 22.0.0 |
| pnpm | 10.33.0 |
| TypeScript | ≥ 5.0 |
| ESLint | ≥ 10 |
Tip
If you use corepack, running corepack enable in the repo root will automatically configure the correct pnpm version.
- Fork the repository on GitHub.
- Clone your fork locally:
git clone https://github.com/<your-username>/eslint-react.git cd eslint-react
- Install dependencies:
pnpm install
- Build the project (this also runs on
prepare):pnpm run build
- Run tests to verify everything works:
pnpm run test
.
├── .github/ # GitHub templates, workflows, and community files
├── .pkgs/ # Internal monorepo packages (not published)
│ ├── configs/ # Shared ESLint, TypeScript, and typedoc configs
│ └── eff/ # Internal Effect library wrapper
├── apps/
│ └── website/ # Documentation website
├── examples/ # Example projects (Next.js, Preact, React DOM, etc.)
├── packages/
│ ├── core/ # @eslint-react/core — core analysis logic
│ ├── shared/ # @eslint-react/shared — shared types and utilities
│ ├── utilities/ # Utility packages (ast, jsx, kit, var)
│ └── plugins/ # Published ESLint plugins
│ ├── eslint-plugin/ # Unified plugin (@eslint-react/eslint-plugin)
│ ├── eslint-plugin-react-x/ # Core React rules (renderer-agnostic)
│ ├── eslint-plugin-react-dom/ # DOM-specific rules
│ ├── eslint-plugin-react-jsx/ # JSX rules
│ ├── eslint-plugin-react-rsc/ # React Server Components rules
│ ├── eslint-plugin-react-web-api/ # Web API rules
│ ├── eslint-plugin-react-naming-convention/ # Naming convention rules
│ └── eslint-plugin-react-debug/ # Debug plugin
├── scripts/ # Automation scripts (scaffold, rename, verify, etc.)
└── test/ # Shared test infrastructure and fixtures
# Full build (update metadata + build internal packages + build all packages + build docs)
pnpm run build
# Build only internal monorepo packages (.pkgs/*)
pnpm run build:pkgs
# Build only publishable packages (packages/*)
pnpm run build:packages
# Build documentation for all packages
pnpm run build:docs
# Build the website
pnpm run build:website# Run all tests
pnpm run test
# Run a single test file
pnpm vitest related plugins/eslint-plugin-react-x/src/rules/<rule-name>/<rule-name>.spec.ts
# Run tests matching a pattern
pnpm vitest run -t "<test-name-pattern>"# Check formatting (dprint)
pnpm run format:check
# Fix formatting
pnpm run format:write
# Run all linting checks (deps, publish, TypeScript, ESLint, examples)
pnpm run lint# Scaffold a new rule (generates boilerplate files)
pnpm run scaffold:rule
# Rename an existing rule
pnpm run rename:rule
# Verify preset configs are consistent
pnpm run verify:configs
# Verify rule documentation matches source metadata
pnpm run verify:rule-docsWe use GitHub Issues to track bugs, feature requests, and rule requests. Before creating a new issue, please:
- Search existing issues — your problem or idea may have already been reported.
- Verify the version — make sure the issue is reproducible with the latest stable release.
- Use the appropriate template:
- 🐛 Bug Report — for unexpected behavior or errors.
- ✨ Feature Request — for new capabilities or improvements.
- 📏 Rule Request — for proposing a new lint rule.
- 📝 Docs Report — for documentation issues or improvements.
- Provide a minimal reproduction — a concise code snippet or repository that demonstrates the issue.
- Discuss first: For new features or new rules, open an issue to discuss the proposal before writing code.
- Sign your commits: All commits must be signed.
- Keep PRs focused: Each pull request should address a single concern.
- Small commits are fine: We squash commits before merging.
Use a descriptive title that includes the type of change:
| Type | Title Format | Example |
|---|---|---|
| Bug fix | fix: description |
fix: false positive in no-array-index-key |
| New feature or rule | feat: description |
feat: add no-unstable-default-props rule |
| Documentation | docs: description |
docs: improve no-direct-mutation-state examples |
| Refactor | refactor: description |
refactor: simplify JSX element analysis |
| Chore | chore: description |
chore: update dependencies |
If the change references an issue, include it: fix: false positive in no-array-index-key (fix: #1234).
- Run
pnpm run buildto ensure the project builds. - Run
pnpm run testto ensure all tests pass. - Run
pnpm run lintto ensure code quality checks pass. - Run
pnpm run format:writeto ensure consistent formatting.
If there isn't already an issue for the rule, create one using the Rule Request template so the proposal can be discussed before implementation.
Use the built-in scaffolding script to generate the boilerplate:
pnpm run scaffold:ruleThis creates the following files inside the target plugin:
packages/<plugin>/src/rules/<rule-name>/
├── <rule-name>.ts # Rule implementation
├── <rule-name>.spec.ts # Test file
└── <rule-name>.mdx # Documentation (MDX with YAML frontmatter)
Write the rule logic in <rule-name>.ts. The file should export:
RULE_NAME— the kebab-case rule name.RULE_FEATURES— metadata about the rule's capabilities.MessageID— a string union type for all message IDs.- A default export calling
createRule(...).
Refer to existing rules (e.g., plugins/eslint-plugin-react-x/src/rules/no-array-index-key/) as a reference.
Add test cases in <rule-name>.spec.ts:
- Use the shared
ruleTester(orruleTesterWithTypesfor type-aware rules) fromtest/. - Use the
dedenttagged template literal for inline test code. - Cover both
validandinvalidcases with appropriatemessageIdassertions. - Consider edge cases such as JSX variants, different component patterns, and TypeScript-specific syntax.
Write the rule documentation in <rule-name>.mdx:
- Include YAML frontmatter with
titleanddescription. - Document the rule under both the individual plugin name and the unified plugin name.
- List applicable presets.
- Provide Rule Details, Common Violations (with examples of invalid and valid code), Resources, and Further Reading sections.
- Export the rule in the plugin's entry file (
src/plugin.ts). - If the rule should be enabled by default, update the preset configurations.
- Update the unified plugin (
plugins/@eslint-react/eslint-plugin/) to include the new rule. - Run
pnpm run update:allto synchronize metadata across the monorepo.
pnpm run build
pnpm run test
pnpm run lint
pnpm run verify:configs
pnpm run verify:rule-docsSubmit a pull request with a clear description linking back to the original issue.
- Test runner: Vitest
- Rule tester: @typescript-eslint/rule-tester
- Test location: Tests are co-located with source files (
<rule-name>.spec.tsalongside<rule-name>.ts).
The shared test setup lives in the test/ directory at the project root:
| File | Description |
|---|---|
test/index.ts |
Barrel export for helpers and rule tester |
test/helpers.ts |
Provides getFixturesRootDir() for fixture path resolution |
test/rule-tester.ts |
Configures ruleTester (standard) and ruleTesterWithTypes (type-aware) |
test/fixtures/ |
Shared fixture files and multiple tsconfig.json variants (JSX modes, etc.) |
import { ruleTester } from "../../../../../../test";
import rule, { RULE_NAME } from "./my-rule";
ruleTester.run(RULE_NAME, rule, {
valid: [
// ... valid test cases
],
invalid: [
// ... invalid test cases with expected messageId
],
});- Use
ruleTesterfor rules that do not require type information. - Use
ruleTesterWithTypesfor rules that do require type information. - Test fixtures support multiple TypeScript JSX configurations (e.g.,
jsx-preserve,jsx-react,jsx-react-native,jsx-preact).
- Formatting: Managed by dprint. Run
pnpm run format:writebefore committing. - Linting: ESLint with TypeScript support. Run
pnpm run lintto check. - Type checking: Run
pnpm run lint:tsto verify there are no type errors.
This project follows the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to the contact listed in the Code of Conduct.