Skip to main content

jsx-no-jsx-as-prop

Disallow render-local JSX allocations passed through JSX props.

Deprecated

  • Lifecycle: Deprecated, frozen, and non-recommended.
  • Deprecated since: v3.0.0
  • Available until: v4.0.0
  • Use instead: no-unstable-react-values

The replacement consolidates JSX, function, array, and object prop stability checks behind one rule and one intrinsic-element policy. Do not enable both.

Targeted pattern scope

This rule reports JSX elements and fragments used as prop expressions inside a function or class render scope. It follows logical and conditional branches and resolves a single same-function const initializer.

Normal JSX children are not props for this rule and are not reported. Module-level JSX constants are treated as stable. Intrinsic attributes can be exempted by name.

What this rule reports

A JSX expression creates a new React element object. Passing that object through a prop can defeat shallow comparison when the receiver is memoized and the element is otherwise stable. Composition through children is often clearer, but JSX-as-prop APIs can also be intentional.

Why this rule exists

Element objects participate in reference comparisons just like arrays and objects. The rule exposes JSX-valued props when a component API expects their identities to remain stable.

❌ Incorrect

function Page() {
return <Layout header={<Header />} />; // New element object every render.
}
function Page() {
const header = <Header />;
return <Layout header={header} />; // The render-local const is still unstable.
}

✅ Correct

function Page() {
return (
<Layout>
<Header /> {/* Composition is outside this rule's prop-specific scope. */}
</Layout>
);
}
const header = <Header />;

function Page() {
return <Layout header={header} />;
}

Behavior and migration notes

This rule reports only. Moving JSX outside a component can accidentally freeze props or context, while adding useMemo can introduce unnecessary dependencies.

React Compiler can automatically memoize values and components in supported builds. Enable this opt-in rule only when a component API deliberately requires stable element identity. It is included by both the all and allStrict presets.

Options

interface Options {
nativeAllowList?: "all" | readonly string[];
}

Default: { nativeAllowList: "all" }

nativeAllowList ignores case-insensitive attribute names on intrinsic JSX elements. The default "all" keeps the rule focused on component props because intrinsic attributes do not participate in a child component's prop-identity contract. Set it to [] to check every intrinsic attribute, or provide selected names to ignore.

ESLint flat config example

import etcMisc from "eslint-plugin-etc-misc";

export default [
{
plugins: { "etc-misc": etcMisc },
rules: {
"etc-misc/jsx-no-jsx-as-prop": "warn",
},
},
];

When not to use it

Disable this rule for render-prop or slot APIs where JSX-valued props are the intended interface, when receiver identity does not affect rendering, or when React Compiler owns memoization.

Package documentation

The rule is a clean-room implementation informed by eslint-plugin-react-perf. It intentionally remains outside the recommended preset because JSX-valued props are often valid API design.

Rule catalog ID: R016

Further reading

Adoption resources

  • Start at warning level in CI, then move to error after cleanup.
  • Review whether stable identity is observable before changing the component API.