What's Changed
✨ New
@eslint-react/eslint-plugin: added theall-type-checkedpreset, which extendsallwith the rules that require type information. (#2095)@eslint-react/eslint-plugin: added the experimental@eslint-react/unstable-exhaustive-depsrule, forked fromexhaustive-deps, with ahooksoption modeled on Biome'suseExhaustiveDependencies(closureIndex/dependenciesIndexfor custom Hooks, andstableResultfor stable results). The rule lives in the unified plugin's ownsrc/ruleswhile incubating and is not enabled by any preset. (#1698, #2078) Thanks to @dgesteves!react-x/set-state-in-effect: reworked around explicit effect roots, recursive value resolution, and a synchronous call graph. Stable aliases, multi-level helpers, named setups, IIFEs, anduseCallback/useMemo/useEffectEventwrappers now share one resolution path; indirect diagnostics point at the effect-side trigger and dedupe by setter sink; async functions remain deferred. (#2075)
🐞 Fixes
react-x/error-boundaries: optional-chainedcreateElementcalls are recognized as JSX-like return values. (#2084)react-x/no-direct-mutation-state: state assignments in nested constructor blocks (if, loops,try, and bare blocks) are no longer reported. (#2088)react-x/no-duplicate-key: only keys that provably have a duplicate are now reported. Unique siblings in a partially duplicate group (ex:[A, B, B]) are no longer reported, and duplicates followed by a unique key (ex:[A, A, B]) are now correctly reported; keys collected throughChildren.toArray()andChildren.map()are no longer reported; only elements returned by.map()callbacks are checked; literal keys are no longer overwritten by later keys; and.map()calls on array literals with at most one element are skipped. (#2108)react-x/no-duplicate-key: the fast-path text check now matches barekeyattributes and keys separated from=by whitespace (ex:<div key />,<div key = {id} />), so such elements are no longer silently skipped.react-x/no-missing-component-display-name:memocomponents passed to generic HOCs are recognized, so anonymous ones require a display name. (#2084)react-x/no-missing-context-display-name: the autofix now applies to exported contexts and no longer produces invalid syntax for multi-declarator declarations. (#2088, #2089)react-x/no-missing-key: elements with a spread attribute are no longer reported as missingkey, and TypeScript-wrapped iterator callbacks are unwrapped before checking. (#2086, #2088)react-x/no-nested-component-definitions: array pattern element defaults andmemopropsAreEqualcomparators are no longer reported as nested components; optional-chainedcreateElementreturns are recognized. (#2084)react-x/no-unstable-context-valueandreact-x/no-unstable-default-props: shadowedBoolean(),String(), andNumber()calls are no longer reported. (#2089)react-x/no-unused-class-component-members: statically resolvable computed member reads and destructuring are counted as usages. (#2088)react-x/refs: non-null guards whose terminating branch is atrystatement, an all-terminatingswitchwith adefaultclause, or an all-terminatingwhile (true)/for (;;)/do...whileloop are now recognized, so ref writes reached only when the ref is null are no longer reported. (#2093, #2109)react-x/set-state-in-effect: TypeScript-wrapped setups are resolved; zero-argument setters called directly in the setup are reported; timer or event callbacks nested inuseCallback/useEffectEventare no longer treated as synchronous setters; every possible setter is reported when the callee may resolve to more than one; the ref-derived exemption applies to any argument position of a custom state hook's setter; ref-derived early-return guards gate the following statements insidetry/switch/bare blocks and loop bodies or update clauses; ref provenance follows IIFEs; nested and outer effect setups are analyzed independently; and setter calls reached through the same effect-side trigger are reported once per setter name. (#2075, #2082, #2083)react-x/static-components: the render-boundary check now requires that at least one declaration of the component binding lives inside a render body, instead of only checking the first declaration. (#2110)react-x/use-state: calls in computed object property keys and JSX subtrees are now detected by the lazy-initialization check. (#2080)react-jsx/no-children-prop-with-children:childrensupplied through a statically resolvable computed key in acreateElementprops object is now detected. (#2086)react-jsx/no-children-prop:childrensupplied through a statically resolvable computed key in acreateElementprops object is now detected, and the suggestion to movechildreninto element content is no longer offered when a following spread attribute could override it. (#2086, #2102)react-jsx/no-leaked-semicolon: leaked semicolons between a JSX child and its parent's closing tag on the same line are now detected. (#2102)react-jsx/no-useless-fragment: fragments with a spread attribute are no longer reported, and lone-CR whitespace, including , is treated as formatting padding like LF and CRLF. (#2086, #2092)react-dom/no-dangerously-set-innerhtml-with-children: empty expression containers and comment-only children no longer count as children; lone-CR whitespace, including , no longer counts either. (#2086, #2092)react-dom/no-hydrateandreact-dom/no-render: autofixes now skip calls with extra or spread arguments and calls whose return values are used, and no longer prepend the new import at the top of the file — existing imports are reused, duplicates are skipped, and new imports are placed after file-level directives, header comments, and existing imports. (#2103, #2104)react-dom/no-script-url: namespaced attributes such asxlink:hrefare now checked forjavascript:URLs. (#2086)react-dom/no-unsafe-iframe-sandbox:sandboxpermissions are matched as whole whitespace-separated tokens, fixing false positives likesandbox="not-allow-scripts". (#2112)react-dom/no-unsafe-target-blank: the suggestion for a missingrelattribute now inserts it after the last JSX attribute, so a spread attribute cannot override the suggested value. (#2086)react-dom/no-use-form-state: autofix-added imports now reuse existing imports, avoid duplicates, and follow file-level directives, header comments, and existing imports. (#2104)react-dom/no-void-elements-with-children: comment-only/empty containers, newline-only formatting whitespace, and lone-CR whitespace, including , no longer count as children. (#2086, #2092)react-web-api/no-leaked-event-listener: dot access and static computed access (including simple template-literal keys) now match as the same property; optional-chained listeners, event types, and receivers pair with their cleanup; string-literal and template-literal option keys are recognized; cyclicsignalalias chains no longer recurse forever; and function-parameter callees are no longer mistaken for the enclosing function. (#2085, #2092, #2093, #2100)react-web-api/no-leaked-fetch:signaloption keys written as string literals or simple template literals are recognized. (#2093)react-web-api/no-leaked-intersection-observerandreact-web-api/no-leaked-resize-observer:observe(...)andunobserve(...)now pair on optional-chained elements. (#2093)react-debug/function-component: only the first argument ofmemo/forwardRefis treated as the wrapped component;propsAreEqualcomparators returning JSX are no longer reported, and functions in that position no longer inherit the surrounding binding or wrapper flags. Array pattern element defaults are no longer reported, and optional-chainedcreateElementreturns are recognized. (#2084)
🪄 Improvements
-
Added fast-path text checks so rules skip AST traversal when their target term is absent from the file. (#2088)
Affected rules:
react-x/no-children-countreact-x/no-children-for-eachreact-x/no-children-mapreact-x/no-children-onlyreact-x/no-children-to-arrayreact-x/no-clone-elementreact-x/no-direct-mutation-statereact-x/no-implicit-childrenreact-x/no-implicit-keyreact-x/no-implicit-refreact-x/no-nested-lazy-component-declarationsreact-x/no-unused-class-component-members
-
JSX and
createElementrules no longer resolve spread props, since a spread's contents are unknown; only direct attributes and properties are inspected. (#2086)Affected rules:
react-x/no-missing-keyreact-jsx/no-children-propreact-jsx/no-children-prop-with-childrenreact-jsx/no-useless-fragmentreact-dom/no-dangerously-set-innerhtmlreact-dom/no-dangerously-set-innerhtml-with-childrenreact-dom/no-missing-button-typereact-dom/no-missing-iframe-sandboxreact-dom/no-string-style-propreact-dom/no-unsafe-iframe-sandboxreact-dom/no-unsafe-target-blankreact-dom/no-void-elements-with-children
-
react-x/no-unknown-property: attribute-tag, DOM-property, and standard-name lookups are cached per React version and resolved lazily. (#2086) -
react-x/set-state-in-effectandreact-x/set-state-in-render: setters are recognized only as the second element of auseStatearray destructuring; tuple-index access and object-pattern numeric keys are no longer detected. (#2075)
📝 Documentation
- Added a TanStack Redact example and updated the website's community projects list. (#2079)
- Added known-issues references for
eslint-plugin-react-jsx(#2102),eslint-plugin-react-dom(#2103),eslint-plugin-react-web-api, andeslint-plugin-react-x, and removed their next-step sections. - Corrected the verification commands in the rule-rename checklist. (#2110)
- Documented known limitations and workarounds for
react-jsx/no-comment-textnodes,react-jsx/no-key-after-spread, andreact-jsx/no-leaked-dollar. (#2102) - Synced the migration tables with rule features and updated the configuration and third-party plugin docs. (#2106)
- Synced the website recipe snippets with the hardened sample rules, and corrected the
noExplicitSpreadPropsrecipe to state that it provides no autofix. (#2089, #2107) - Updated the FAQ, glossary, examples pages, the contributing guide's monorepo architecture diagram and package/example listings, and the term-based rule implementation patterns.
🏗️ Internal
@eslint-react/ast,@eslint-react/core, and@local/testkit: added tests for AST tree types, core semantic interfaces, and type utilities.@eslint-react/ast: added theCollectandAnalyzenamespaces andTraverse.simpleTraverse;Analyze.isUnconditionallyTerminatingnow handlestrystatements, terminatingswitchstatements with adefaultclause, and terminatingwhile (true)/for (;;)/do...whileloops;Compare.isEqualnow comparesChainExpression, regex literals, and membercomputed/optionalflags;Collectnow traversesforinitializers and updates; renamedCheck.isConditionaltoCheck.isConditionalOrLoop; addedCheck.isArrayPatternElementDefault,Check.isObjectPatternElementDefault,Check.isLoopStatement, andExtract.getJSXName; improved private identifier handling; removed the general-purposeExtract.getMemberChain,Extract.getPropertyName,Extract.findProperty, andExtract.getAssignmentTargetshelpers in favor ofresolveAssignmentTargetsfrom@eslint-react/varand rule-local traversal. (#2080, #2083, #2084, #2086, #2092, #2093, #2099, #2100)@eslint-react/core: moved thecreateElementhelpers to@eslint-react/jsx; component detection now checks wrapper argument positions and unwraps chain expressions; improved render-method detection and qualified type names; narrowed the accepted node types of hook/component detection helpers. (#2075, #2077, #2084, #2099)@eslint-react/var: addedresolveOriginChain,isProvenanceEqual,hasDefault, andresolveAssignmentTargets; expandedresolveObjectTypeandcreateImportLookup, which now exposesisModuleBindinginstead ofhasNamespace; madeisValueEqualidentity-only (useisProvenanceEqualfor structural provenance); and removedresolveBindingTarget/BindingTarget, with binding targets now unwrapped inline. (#2085, #2086, #2088, #2100)@local/samples: hardened the sample rules. (#2107)@local/testkit: added averifyAndFixhelper for testing rules with autofixes. (#2078)- Bumped dependencies across the workspace, including
eslintto10.14.0,typescript-eslintto8.72.1,nextto16.4.5,vitestto5.1.0,tsdownto0.23.5,nxto24.0.4,oxlintto1.91.0,viteto8.3.6,effectto4.1.2, and thefumadocspackages to16.18.0. - Removed the now-unused
@typescript-eslint/typescript-estreedependency from@eslint-react/ast,@eslint-react/core,@eslint-react/var, andeslint-plugin-react-x. (#2080) - Removed the unused
inlinedDependenciesfield from the monorepo package configuration. - Unified test language options and reorganized the test harness: tsconfig fixtures replace the per-JSX-emit tester factories, and untyped parsing disables typescript-estree's single-run inference. (#2105)
- Enforced a 1-day dependency maturity period; served README assets from the jsDelivr CDN; restricted website deployments to stable releases via a Vercel deploy hook; refactored the publish workflow trigger and pinned zizmor through its official action.
New Contributors
- Diogo Esteves (@dgesteves) made their first contribution in #2078.
Full Changelog: v5.28.8...v5.30.0-rc.0
Attestation
https://github.com/Rel1cx/eslint-react/attestations/64547490