Repository: jsx-eslint/eslint-plugin-react (js)

Issue:
[Bug]: Unknown property 'precedence' found (react/no-unknown-property)
### Is there an existing issue for this?

- [X] I have searched the existing issues and my issue is unique
- [X] My issue appears in the command-line and not only in the text editor

### Description Overview

<!--Brief description-->

react v19 introduces a new `precedence` string prop to the [`<link>`](https://react.dev/reference/react-dom/components/link#props) and [`<style>`](https://react.dev/reference/react-dom/components/style#props) elements, but the `react/no-unknown-property` rule doesn’t yet know about it and generates linting errors when used:

```jsx
<>
    <link href="https://foo.bar" precedence="medium" rel="canonical" />
    <style href="unique-hash" precedence="anything">{` p { color: red; } `}</style>
</>
```

<!--What is happening? / What is the error?-->

example error:
```
  211:39  error  Unknown property 'precedence' found  react/no-unknown-property
```

<!--What command(s) did you run to reproduce issue?-->

these occur when running my `lint` npm run script: `"lint": "eslint .",`


### Expected Behavior

<!--Brief description-->

i expect the new `precedence` attribute to not be considered an “unknown” property


### eslint-plugin-react version

v7.36.1

### eslint version

v8.57.0

### node version

v22.7.0

Pull request:
[Fix] `no-unknown-property` support new `precedence` prop
Fixes #3827

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b2b5bcf..8497f49c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 ### Fixed
 * [`no-danger`]: avoid a crash on a nested component name ([#3833][] @ljharb)
 * [Fix] types: correct generated type declaration ([#3840][] @ocavue)
+* [`no-unknown-property`]: support `precedence` prop in react 19 ([#3829][] @acusti)
 
 ### Changed
 * [Tests] [`jsx-no-script-url`]: Improve tests ([#3849][] @radu2147)
@@ -19,6 +20,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 [#3841]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3841
 [#3840]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3840
 [#3833]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3833
+[#3829]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3829
 
 ## [7.37.2] - 2024.10.22
 
diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js
index dc501800..9a21c96f 100644
--- a/lib/rules/no-unknown-property.js
+++ b/lib/rules/no-unknown-property.js
@@ -363,16 +363,24 @@ const REACT_ON_PROPS = [
 ];
 
 function getDOMPropertyNames(context) {
-  const ALL_DOM_PROPERTY_NAMES = DOM_PROPERTY_NAMES_TWO_WORDS.concat(DOM_PROPERTY_NAMES_ONE_WORD);
-  // this was removed in React v16.1+, see https://github.com/facebook/react/pull/10823
-  if (!testReactVersion(context, '>= 16.1.0')) {
-    return ALL_DOM_PROPERTY_NAMES.concat('allowTransparency');
-  }
-  // these were added in React v16.4.0, see https://reactjs.org/blog/2018/05/23/react-v-16-4.html and https://github.com/facebook/react/pull/12507
-  if (testReactVersion(context, '>= 16.4.0')) {
-    return ALL_DOM_PROPERTY_NAMES.concat(REACT_ON_PROPS);
-  }
-  return ALL_DOM_PROPERTY_NAMES;
+  return [].concat(
+    DOM_PROPERTY_NAMES_TWO_WORDS,
+    DOM_PROPERTY_NAMES_ONE_WORD,
+
+    testReactVersion(context, '>= 16.1.0') ? [].concat(
+      testReactVersion(context, '>= 16.4.0') ? [].concat(
+        // these were added in React v16.4.0, see https://reactjs.org/blog/2018/05/23/react-v-16-4.html and https://github.com/facebook/react/pull/12507
+        REACT_ON_PROPS,
+        testReactVersion(context, '>= 19') ? [
+          // precedence was added in React v19, see https://react.dev/blog/2024/04/25/react-19#support-for-stylesheets
+          'precedence',
+        ] : []
+      ) : []
+    ) : [
+      // this was removed in React v16.1+, see https://github.com/facebook/react/pull/10823
+      'allowTransparency',
+    ]
+  );
 }
 
 // ------------------------------------------------------------------------------
@@ -501,6 +509,7 @@ function getStandardName(name, context) {
     return SVGDOM_ATTRIBUTE_NAMES[/** @type {keyof SVGDOM_ATTRIBUTE_NAMES} */ (name)];
   }
   const names = getDOMPropertyNames(context);
+
   // Let's find a possible attribute match with a case-insensitive search.
   return names.find((element) => element.toLowerCase() === name.toLowerCase());
 }


Test patch:
diff --git a/tests/lib/rules/no-unknown-property.js b/tests/lib/rules/no-unknown-property.js
index 32e6c6d6..97ae42ea 100644
--- a/tests/lib/rules/no-unknown-property.js
+++ b/tests/lib/rules/no-unknown-property.js
@@ -89,6 +89,12 @@ ruleTester.run('no-unknown-property', rule, {
     { code: '<div onPointerDown={this.onDown} onPointerUp={this.onUp} />' },
     { code: '<input type="checkbox" defaultChecked={this.state.checkbox} />' },
     { code: '<div onTouchStart={this.startAnimation} onTouchEnd={this.stopAnimation} onTouchCancel={this.cancel} onTouchMove={this.move} onMouseMoveCapture={this.capture} onTouchCancelCapture={this.log} />' },
+    {
+      code: '<link precedence="medium" href="https://foo.bar" rel="canonical" />',
+      settings: {
+        react: { version: '19.0.0' },
+      },
+    },
     // Case ignored attributes, for `charset` discussion see https://github.com/jsx-eslint/eslint-plugin-react/pull/1863
     { code: '<meta charset="utf-8" />;' },
     { code: '<meta charSet="utf-8" />;' },