diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs
index f1b9b653b..39ca8ba8c 100644
--- a/crates/oxc_linter/src/rules.rs
+++ b/crates/oxc_linter/src/rules.rs
@@ -126,6 +126,7 @@ mod jest {
mod react {
pub mod jsx_key;
+ pub mod jsx_no_duplicate_props;
pub mod jsx_no_useless_fragment;
pub mod no_children_prop;
}
@@ -251,9 +252,10 @@ oxc_macros::declare_all_lint_rules! {
unicorn::no_thenable,
unicorn::throw_new_error,
unicorn::prefer_array_flat_map,
- react::no_children_prop,
react::jsx_key,
+ react::jsx_no_duplicate_props,
react::jsx_no_useless_fragment,
+ react::no_children_prop,
import::named,
import::no_cycle,
import::no_self_import,
diff --git a/crates/oxc_linter/src/rules/react/jsx_no_duplicate_props.rs b/crates/oxc_linter/src/rules/react/jsx_no_duplicate_props.rs
new file mode 100644
index 000000000..2384fc191
--- /dev/null
+++ b/crates/oxc_linter/src/rules/react/jsx_no_duplicate_props.rs
@@ -0,0 +1,117 @@
+use oxc_ast::{
+ ast::{JSXAttributeItem, JSXAttributeName},
+ AstKind,
+};
+use oxc_diagnostics::{
+ miette::{self, Diagnostic},
+ thiserror::{self, Error},
+};
+use oxc_macros::declare_oxc_lint;
+use oxc_span::{Atom, Span};
+use rustc_hash::FxHashMap;
+
+use crate::{context::LintContext, rule::Rule, AstNode};
+
+#[derive(Debug, Error, Diagnostic)]
+#[error(
+ "eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop \"{0}\" is duplicated."
+)]
+#[diagnostic(
+ severity(warning),
+ help("Remove one of the props, or rename them so each prop is distinct.")
+)]
+struct JsxNoDuplicatePropsDiagnostic(Atom, #[label] pub Span, #[label] pub Span);
+
+#[derive(Debug, Default, Clone)]
+pub struct JsxNoDuplicateProps;
+
+declare_oxc_lint!(
+ /// ### What it does
+ ///
+ /// This rule prevents duplicate props in JSX elements.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// Having duplicate props in a JSX element is most likely a mistake.
+ /// Creating JSX elements with duplicate props can cause unexpected behavior in your application.
+ ///
+ /// ### Example
+ /// ```javascript
+ /// // Bad
+ /// ;
+ /// ;
+ ///
+ /// // Good
+ /// ;
+ /// ;
+ ///
+ /// ```
+ JsxNoDuplicateProps,
+ correctness
+);
+
+impl Rule for JsxNoDuplicateProps {
+ fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ let AstKind::JSXOpeningElement(jsx_opening_elem) = node.kind() else { return };
+
+ let mut props: FxHashMap = FxHashMap::default();
+
+ for attr in &jsx_opening_elem.attributes {
+ let JSXAttributeItem::Attribute(jsx_attr) = attr else { continue };
+
+ let JSXAttributeName::Identifier(ident) = &jsx_attr.name else { continue };
+
+ if let Some(old_span) = props.insert(ident.name.clone(), ident.span) {
+ ctx.diagnostic(JsxNoDuplicatePropsDiagnostic(
+ ident.name.clone(),
+ old_span,
+ ident.span,
+ ));
+ }
+ }
+ }
+}
+
+#[test]
+fn test() {
+ use crate::tester::Tester;
+
+ let pass = vec![
+ (";", None),
+ (";", None),
+ (";", None),
+ (";", None),
+ (";", None),
+ (";", None),
+ (r#";"#, None),
+ (r#";"#, None),
+ (r#";"#, None),
+ (";", None),
+ (";", None),
+ (r#";"#, None),
+ ];
+
+ let fail = vec![
+ (";", None),
+ (";", None),
+ (r#";"#, None),
+ (r#";"#, None),
+ (r#";"#, None),
+ (r#";"#, None),
+ (r#";"#, None),
+ (r#";"#, None),
+ (
+ r#"
+ ;
+ "#,
+ None,
+ ),
+ ];
+
+ Tester::new(JsxNoDuplicateProps::NAME, pass, fail).test_and_snapshot();
+}
diff --git a/crates/oxc_linter/src/snapshots/jsx_no_duplicate_props.snap b/crates/oxc_linter/src/snapshots/jsx_no_duplicate_props.snap
new file mode 100644
index 000000000..3dd982f48
--- /dev/null
+++ b/crates/oxc_linter/src/snapshots/jsx_no_duplicate_props.snap
@@ -0,0 +1,73 @@
+---
+source: crates/oxc_linter/src/tester.rs
+expression: jsx_no_duplicate_props
+---
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "A" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:1:1]
+ 1 │ ;
+ · ─ ─
+ ╰────
+ help: Remove one of the props, or rename them so each prop is distinct.
+
+ ⚠ eslint-plugin-react(jsx-no-duplicate-props): No duplicate props allowed. The prop "a" is duplicated.
+ ╭─[jsx_no_duplicate_props.tsx:2:1]
+ 2 │