diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs
index 288e022a4..84110abd0 100644
--- a/crates/oxc_linter/src/rules.rs
+++ b/crates/oxc_linter/src/rules.rs
@@ -163,6 +163,13 @@ mod react {
pub mod require_render_return;
}
+mod react_perf {
+ pub mod no_jsx_as_prop;
+ pub mod no_new_array_as_prop;
+ pub mod no_new_function_as_props;
+ pub mod no_new_object_as_prop;
+}
+
mod unicorn {
pub mod catch_error_name;
pub mod empty_brace_spaces;
@@ -508,6 +515,10 @@ oxc_macros::declare_all_lint_rules! {
react::no_is_mounted,
react::no_unknown_property,
react::require_render_return,
+ react_perf::no_jsx_as_prop,
+ react_perf::no_new_array_as_prop,
+ react_perf::no_new_function_as_props,
+ react_perf::no_new_object_as_prop,
import::default,
import::no_named_as_default_member,
import::named,
diff --git a/crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs
new file mode 100644
index 000000000..2fa131d5a
--- /dev/null
+++ b/crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs
@@ -0,0 +1,95 @@
+use oxc_ast::{
+ ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer},
+ AstKind,
+};
+use oxc_diagnostics::{
+ miette::{self, Diagnostic},
+ thiserror::Error,
+};
+use oxc_macros::declare_oxc_lint;
+use oxc_span::Span;
+
+use crate::{context::LintContext, rule::Rule, utils::get_prop_value, AstNode};
+
+#[derive(Debug, Error, Diagnostic)]
+#[error(
+ "eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX."
+)]
+#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))]
+struct NoJsxAsPropDiagnostic(#[label] pub Span);
+
+#[derive(Debug, Default, Clone)]
+pub struct NoJsxAsProp;
+
+declare_oxc_lint!(
+ /// ### What it does
+ ///
+ /// Prevent JSX that are local to the current method from being used as values of JSX props
+ ///
+ /// ### Example
+ /// ```javascript
+ /// // Bad
+ /// } />
+ /// } />
+ /// } />
+ ///
+ /// // Good
+ ///
+ /// ```
+ NoJsxAsProp,
+ restriction
+);
+
+impl Rule for NoJsxAsProp {
+ fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ if let AstKind::JSXElement(jsx_elem) = node.kind() {
+ check_jsx_element(jsx_elem, ctx);
+ }
+ }
+}
+
+fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
+ for item in &jsx_elem.opening_element.attributes {
+ match get_prop_value(item) {
+ None => return,
+ Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
+ expression: JSXExpression::Expression(expr),
+ ..
+ })) => {
+ if let Some(span) = check_expression(expr) {
+ ctx.diagnostic(NoJsxAsPropDiagnostic(span));
+ }
+ }
+ _ => {}
+ };
+ }
+}
+
+fn check_expression(expr: &Expression) -> Option {
+ match expr.without_parenthesized() {
+ Expression::JSXElement(expr) => Some(expr.span),
+ Expression::LogicalExpression(expr) => {
+ check_expression(&expr.left).or_else(|| check_expression(&expr.right))
+ }
+ Expression::ConditionalExpression(expr) => {
+ check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate))
+ }
+ _ => None,
+ }
+}
+
+#[test]
+fn test() {
+ use crate::tester::Tester;
+
+ let pass = vec![r" "];
+
+ let fail = vec![
+ r" } />",
+ r" } />",
+ r" } />",
+ r" )} />",
+ ];
+
+ Tester::new(NoJsxAsProp::NAME, pass, fail).test_and_snapshot();
+}
diff --git a/crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs
new file mode 100644
index 000000000..bc45773d8
--- /dev/null
+++ b/crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs
@@ -0,0 +1,117 @@
+use oxc_ast::{
+ ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer},
+ AstKind,
+};
+use oxc_diagnostics::{
+ miette::{self, Diagnostic},
+ thiserror::Error,
+};
+use oxc_macros::declare_oxc_lint;
+use oxc_span::Span;
+
+use crate::{
+ context::LintContext,
+ rule::Rule,
+ utils::{get_prop_value, is_constructor_matching_name},
+ AstNode,
+};
+
+#[derive(Debug, Error, Diagnostic)]
+#[error("eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.")]
+#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))]
+struct NoNewArrayAsPropDiagnostic(#[label] pub Span);
+
+#[derive(Debug, Default, Clone)]
+pub struct NoNewArrayAsProp;
+
+declare_oxc_lint!(
+ /// ### What it does
+ ///
+ /// Prevent Arrays that are local to the current method from being used as values of JSX props
+ ///
+ /// ### Example
+ /// ```javascript
+ /// // Bad
+ ///
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// // Good
+ ///
+ /// ```
+ NoNewArrayAsProp,
+ restriction
+);
+
+impl Rule for NoNewArrayAsProp {
+ fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ if let AstKind::JSXElement(jsx_elem) = node.kind() {
+ check_jsx_element(jsx_elem, ctx);
+ }
+ }
+}
+
+fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
+ for item in &jsx_elem.opening_element.attributes {
+ match get_prop_value(item) {
+ None => return,
+ Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
+ expression: JSXExpression::Expression(expr),
+ ..
+ })) => {
+ if let Some(span) = check_expression(expr) {
+ ctx.diagnostic(NoNewArrayAsPropDiagnostic(span));
+ }
+ }
+ _ => {}
+ };
+ }
+}
+
+fn check_expression(expr: &Expression) -> Option {
+ match expr.without_parenthesized() {
+ Expression::ArrayExpression(expr) => Some(expr.span),
+ Expression::CallExpression(expr) => {
+ if is_constructor_matching_name(&expr.callee, "Array") {
+ Some(expr.span)
+ } else {
+ None
+ }
+ }
+ Expression::NewExpression(expr) => {
+ if is_constructor_matching_name(&expr.callee, "Array") {
+ Some(expr.span)
+ } else {
+ None
+ }
+ }
+ Expression::LogicalExpression(expr) => {
+ check_expression(&expr.left).or_else(|| check_expression(&expr.right))
+ }
+ Expression::ConditionalExpression(expr) => {
+ check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate))
+ }
+ _ => None,
+ }
+}
+
+#[test]
+fn test() {
+ use crate::tester::Tester;
+
+ let pass = vec![r" "];
+
+ let fail = vec![
+ r" ",
+ r" ",
+ r" ",
+ r" ",
+ r" ",
+ r" ",
+ ];
+
+ Tester::new(NoNewArrayAsProp::NAME, pass, fail).test_and_snapshot();
+}
diff --git a/crates/oxc_linter/src/rules/react_perf/no_new_function_as_props.rs b/crates/oxc_linter/src/rules/react_perf/no_new_function_as_props.rs
new file mode 100644
index 000000000..bf04a3198
--- /dev/null
+++ b/crates/oxc_linter/src/rules/react_perf/no_new_function_as_props.rs
@@ -0,0 +1,143 @@
+use oxc_ast::{
+ ast::{
+ Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer,
+ MemberExpression,
+ },
+ AstKind,
+};
+use oxc_diagnostics::{
+ miette::{self, Diagnostic},
+ thiserror::Error,
+};
+use oxc_macros::declare_oxc_lint;
+use oxc_span::Span;
+
+use crate::{
+ context::LintContext,
+ rule::Rule,
+ utils::{get_prop_value, is_constructor_matching_name},
+ AstNode,
+};
+
+#[derive(Debug, Error, Diagnostic)]
+#[error("eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.")]
+#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))]
+struct NoNewFunctionAsPropsDiagnostic(#[label] pub Span);
+
+#[derive(Debug, Default, Clone)]
+pub struct NoNewFunctionAsProps;
+
+declare_oxc_lint!(
+ /// ### What it does
+ ///
+ /// Prevent Functions that are local to the current method from being used as values of JSX props
+ /// ### Example
+ /// ```javascript
+ /// // Bad
+ ///
+ ///
+ ///
+ /// // Good
+ ///
+ /// ```
+ NoNewFunctionAsProps,
+ restriction
+);
+
+impl Rule for NoNewFunctionAsProps {
+ fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ if let AstKind::JSXElement(jsx_elem) = node.kind() {
+ check_jsx_element(jsx_elem, ctx);
+ }
+ }
+}
+
+fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
+ for item in &jsx_elem.opening_element.attributes {
+ match get_prop_value(item) {
+ None => return,
+ Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
+ expression: JSXExpression::Expression(expr),
+ ..
+ })) => {
+ if let Some(span) = check_expression(expr) {
+ ctx.diagnostic(NoNewFunctionAsPropsDiagnostic(span));
+ }
+ }
+ _ => {}
+ };
+ }
+}
+
+fn check_expression(expr: &Expression) -> Option {
+ match expr.without_parenthesized() {
+ Expression::ArrowExpression(expr) => Some(expr.span),
+ Expression::FunctionExpression(expr) => Some(expr.span),
+ Expression::CallExpression(expr) => {
+ if is_constructor_matching_name(&expr.callee, "Function") {
+ return Some(expr.span);
+ }
+
+ let Expression::MemberExpression(member_expr) = &expr.callee else {
+ return None;
+ };
+
+ let property_name = MemberExpression::static_property_name(member_expr);
+
+ if property_name == Some("bind") {
+ Some(expr.span)
+ } else {
+ None
+ }
+ }
+ Expression::NewExpression(expr) => {
+ if is_constructor_matching_name(&expr.callee, "Function") {
+ Some(expr.span)
+ } else {
+ None
+ }
+ }
+ Expression::LogicalExpression(expr) => {
+ check_expression(&expr.left).or_else(|| check_expression(&expr.right))
+ }
+ Expression::ConditionalExpression(expr) => {
+ check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate))
+ }
+ _ => None,
+ }
+}
+
+#[test]
+fn test() {
+ use crate::tester::Tester;
+
+ let pass = vec![
+ r" ",
+ r" ",
+ r" ",
+ r" ",
+ r"var a; ",
+ r"var a;a = 1; ",
+ r"var a; ",
+ r"function foo ({prop1 = function(){}, prop2}) {
+ return
+ }",
+ r"function foo ({prop1, prop2 = function(){}}) {
+ return
+ }",
+ ];
+
+ let fail = vec![
+ r" ",
+ r"- true} />",
+ r"
",
+ r"- ",
+ r"
",
+ r" ",
+ r" ",
+ r" ",
+ r" ",
+ ];
+
+ Tester::new(NoNewFunctionAsProps::NAME, pass, fail).test_and_snapshot();
+}
diff --git a/crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs
new file mode 100644
index 000000000..58eb0a332
--- /dev/null
+++ b/crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs
@@ -0,0 +1,117 @@
+use oxc_ast::{
+ ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer},
+ AstKind,
+};
+use oxc_diagnostics::{
+ miette::{self, Diagnostic},
+ thiserror::Error,
+};
+use oxc_macros::declare_oxc_lint;
+use oxc_span::Span;
+
+use crate::{
+ context::LintContext,
+ rule::Rule,
+ utils::{get_prop_value, is_constructor_matching_name},
+ AstNode,
+};
+
+#[derive(Debug, Error, Diagnostic)]
+#[error("eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.")]
+#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))]
+struct NoNewObjectAsPropDiagnostic(#[label] pub Span);
+
+#[derive(Debug, Default, Clone)]
+pub struct NoNewObjectAsProp;
+
+declare_oxc_lint!(
+ /// ### What it does
+ ///
+ /// Prevent Objects that are local to the current method from being used as values of JSX props
+ ///
+ /// ```javascript
+ /// // Bad
+ ///
+ ///
+ ///
+ ///
+ ///
+ //
+ ///
+ /// // Good
+ ///
+ /// ```
+ NoNewObjectAsProp,
+ restriction
+);
+
+impl Rule for NoNewObjectAsProp {
+ fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
+ if let AstKind::JSXElement(jsx_elem) = node.kind() {
+ check_jsx_element(jsx_elem, ctx);
+ }
+ }
+}
+
+fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
+ for item in &jsx_elem.opening_element.attributes {
+ match get_prop_value(item) {
+ None => return,
+ Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
+ expression: JSXExpression::Expression(expr),
+ ..
+ })) => {
+ if let Some(span) = check_expression(expr) {
+ ctx.diagnostic(NoNewObjectAsPropDiagnostic(span));
+ }
+ }
+ _ => {}
+ };
+ }
+}
+
+fn check_expression(expr: &Expression) -> Option {
+ match expr.without_parenthesized() {
+ Expression::ObjectExpression(expr) => Some(expr.span),
+ Expression::CallExpression(expr) => {
+ if is_constructor_matching_name(&expr.callee, "Object") {
+ Some(expr.span)
+ } else {
+ None
+ }
+ }
+ Expression::NewExpression(expr) => {
+ if is_constructor_matching_name(&expr.callee, "Object") {
+ Some(expr.span)
+ } else {
+ None
+ }
+ }
+ Expression::LogicalExpression(expr) => {
+ check_expression(&expr.left).or_else(|| check_expression(&expr.right))
+ }
+ Expression::ConditionalExpression(expr) => {
+ check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate))
+ }
+ _ => None,
+ }
+}
+
+#[test]
+fn test() {
+ use crate::tester::Tester;
+
+ let pass = vec![r" "];
+
+ let fail = vec![
+ r" ",
+ r" ",
+ r" ",
+ r"",
+ r" ",
+ r" ",
+ r" ",
+ ];
+
+ Tester::new(NoNewObjectAsProp::NAME, pass, fail).test_and_snapshot();
+}
diff --git a/crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap b/crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap
new file mode 100644
index 000000000..07912913b
--- /dev/null
+++ b/crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap
@@ -0,0 +1,33 @@
+---
+source: crates/oxc_linter/src/tester.rs
+expression: no_jsx_as_prop
+---
+ ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX.
+ ╭─[no_jsx_as_prop.tsx:1:1]
+ 1 │ } />
+ · ───────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX.
+ ╭─[no_jsx_as_prop.tsx:1:1]
+ 1 │ } />
+ · ───────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX.
+ ╭─[no_jsx_as_prop.tsx:1:1]
+ 1 │ } />
+ · ───────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX.
+ ╭─[no_jsx_as_prop.tsx:1:1]
+ 1 │ )} />
+ · ───────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+
diff --git a/crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap b/crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap
new file mode 100644
index 000000000..3d2f6c3f8
--- /dev/null
+++ b/crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap
@@ -0,0 +1,47 @@
+---
+source: crates/oxc_linter/src/tester.rs
+expression: no_new_array_as_prop
+---
+ ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.
+ ╭─[no_new_array_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.
+ ╭─[no_new_array_as_prop.tsx:1:1]
+ 1 │
+ · ───────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.
+ ╭─[no_new_array_as_prop.tsx:1:1]
+ 1 │
+ · ───────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.
+ ╭─[no_new_array_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.
+ ╭─[no_new_array_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.
+ ╭─[no_new_array_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+
diff --git a/crates/oxc_linter/src/snapshots/no_new_function_as_props.snap b/crates/oxc_linter/src/snapshots/no_new_function_as_props.snap
new file mode 100644
index 000000000..184183d30
--- /dev/null
+++ b/crates/oxc_linter/src/snapshots/no_new_function_as_props.snap
@@ -0,0 +1,68 @@
+---
+source: crates/oxc_linter/src/tester.rs
+expression: no_new_function_as_props
+---
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │
+ · ───────────────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │ - true} />
+ · ──────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │
+ · ─────────────────────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │ -
+ · ──────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │
+ · ────────────────────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │
+ · ─────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │
+ · ─────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │
+ · ────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.
+ ╭─[no_new_function_as_props.tsx:1:1]
+ 1 │
+ · ────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+
diff --git a/crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap b/crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap
new file mode 100644
index 000000000..3e76bf107
--- /dev/null
+++ b/crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap
@@ -0,0 +1,54 @@
+---
+source: crates/oxc_linter/src/tester.rs
+expression: no_new_object_as_prop
+---
+ ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.
+ ╭─[no_new_object_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.
+ ╭─[no_new_object_as_prop.tsx:1:1]
+ 1 │
+ · ────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.
+ ╭─[no_new_object_as_prop.tsx:1:1]
+ 1 │
+ · ────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.
+ ╭─[no_new_object_as_prop.tsx:1:1]
+ 1 │
+ · ─────────────────
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.
+ ╭─[no_new_object_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.
+ ╭─[no_new_object_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+ ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.
+ ╭─[no_new_object_as_prop.tsx:1:1]
+ 1 │
+ · ──
+ ╰────
+ help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array).
+
+
diff --git a/crates/oxc_linter/src/utils/mod.rs b/crates/oxc_linter/src/utils/mod.rs
index b432d7dbd..117b892b1 100644
--- a/crates/oxc_linter/src/utils/mod.rs
+++ b/crates/oxc_linter/src/utils/mod.rs
@@ -1,6 +1,7 @@
mod jest;
mod node;
mod react;
+mod react_perf;
mod unicorn;
-pub use self::{jest::*, node::*, react::*, unicorn::*};
+pub use self::{jest::*, node::*, react::*, react_perf::*, unicorn::*};
diff --git a/crates/oxc_linter/src/utils/react_perf.rs b/crates/oxc_linter/src/utils/react_perf.rs
new file mode 100644
index 000000000..4f13ee46f
--- /dev/null
+++ b/crates/oxc_linter/src/utils/react_perf.rs
@@ -0,0 +1,8 @@
+use oxc_ast::ast::Expression;
+
+pub fn is_constructor_matching_name(callee: &Expression<'_>, name: &str) -> bool {
+ let Expression::Identifier(ident) = callee else {
+ return false;
+ };
+ ident.name == name
+}