Skip to Content

React

React TSX rules.

This family bundles rules from eslint-plugin-react, eslint-plugin-react-hooks, and eslint-plugin-react-refresh under one namespace, matching Oxlint’s react/* plugin layout.

Performance-only rules live in a separate React performance family because they are opt-in toggles rather than correctness checks.

JSX spread attributes (<Comp {...props} />) carry an unknown prop set, so element-scanning rules read only the attributes written out explicitly next to the spread — the jsx-ast-utils default upstream ESLint uses.

Source: eslint-plugin-react (MIT), eslint-plugin-react-hooks (MIT).

eslint-plugin-react-refresh (MIT).

Rule index

Each rule name links to the detailed section below.

Examples come from the checked lint corpus or package-level rule tests when project layout matters.

Disallow

Other checks

Rules

react/button-has-type

Require explicit, valid type values on JSX <button> elements ("button", "submit", "reset"), the HTML default of "submit" causes accidental form submissions.

Example:

// reports: react/button-has-type (error) const a = <button>Submit</button>;

react/display-name

Require components wrapped in React.memo(...) or React.forwardRef(...) to be named, either by passing a named function, assigning the call to a named binding, or setting an explicit displayName.

Anonymous wrappers leave the resulting component nameless in React DevTools and runtime stack frames.

Example:

import { memo } from "react"; // reports: react/display-name (error) export default memo(() => <div />);

react/exhaustive-deps

Detect high-confidence missing identifiers in React Hook dependency arrays (useEffect, useLayoutEffect, useInsertionEffect, useMemo, useCallback).

Example:

import { useEffect } from "react"; function Component(props: { value: number }) { // reports: react/exhaustive-deps (error) useEffect(() => { console.log(props.value); }, []); return null; }

react/component-hook-factories

Reject declaring a component or custom Hook inside another component or Hook body.

Each render rebuilds the inner function, giving it a new identity that discards its state and breaks memoization.

Example:

import { useState } from "react"; function Component() { // reports: react/component-hook-factories (error) function useNested() { const [value] = useState(0); return value; } return <div>{useNested()}</div>; }

react/iframe-missing-sandbox

Require JSX <iframe> elements to declare a sandbox attribute, which restricts what embedded content can do (scripts, forms, navigation).

Example:

const url = "/embed"; // reports: react/iframe-missing-sandbox (error) const a = <iframe src={url} />;

react/immutability

Reject mutating props, state, or Hook return values inside a component or Hook.

React treats these as read-only; mutating a memoized value can desync renders from the dependencies React tracked it under.

Example:

function Component(props: { value: { count: number } }) { // reports: react/immutability (error) props.value.count = 1; return <div>{props.value.count}</div>; }

react/jsx-key

Require key props on JSX elements produced from arrays or .map(...) calls, React uses the key to track list identity across renders.

Example:

const items = [1, 2, 3]; // reports: react/jsx-key (error) const a = [<span>one</span>, <span>two</span>];

react/jsx-no-duplicate-props

Reject duplicate JSX prop names on the same element, later occurrences silently overwrite earlier ones.

Example:

// reports: react/jsx-no-duplicate-props (error) const a = <div className="x" className="y" />;

react/jsx-no-script-url

Reject javascript: URLs in JSX URL-like props such as href and src.

Such URLs evaluate the URL body as script, the same code-execution surface as eval, and a common XSS vector when the value is user-controlled.

Example:

// reports: react/jsx-no-script-url (error) const a = <a href="javascript:void(0)">click</a>;

react/jsx-no-target-blank

Reject <a target="_blank"> (or any JSX element with target="_blank") that does not also carry rel="noreferrer" (or rel="noopener noreferrer").

Without those tokens the opened page can navigate the originating window through window.opener, a phishing and tab-nabbing vector.

Example:

const url = "https://example.com"; const a = ( // reports: react/jsx-no-target-blank (error) <a href={url} target="_blank"> open </a> );

react/jsx-no-undef

Reject JSX elements whose tag is an uppercase identifier with no value-level declaration anywhere in the source file.

Lowercase tags are intrinsic HTML; qualified <Foo.Bar> forms are skipped because resolving the outer name requires module-level information that belongs to the type checker.

Example:

// reports: react/jsx-no-undef (error) const a = <Missing />;

react/jsx-no-useless-fragment

Reject JSX fragments that wrap exactly one element child or have no meaningful content, the child (or nothing) can be returned directly.

Covers both the short <>...</> form and explicit <Fragment> / <React.Fragment> elements.

Example:

const Child = () => <span>child</span>; const a = ( // reports: react/jsx-no-useless-fragment (error) <> <Child /> </> );

react/no-array-index-key

Reject key={index} patterns inside JSX lists, array index keys reorder incorrectly on insertion.

Example:

const items = ["a", "b", "c"]; // reports: react/no-array-index-key (error) const a = items.map((value, index) => <li key={index}>{value}</li>);

react/no-children-prop

Reject passing children through an explicit children JSX prop.

The nested-tag form is shorter, mirrors HTML, and avoids the double-children footgun where a children prop and nested JSX both target the same slot.

Example:

const Box = (props: { children?: unknown }) => <section>{props.children}</section>; // reports: react/no-children-prop (error) const a = <Box children="hello" />;

react/no-danger

Reject dangerouslySetInnerHTML altogether.

The prop bypasses React’s escaping and injects raw HTML into the DOM, a common XSS vector when the input is not sanitized upstream.

Example:

const html = "<strong>trusted</strong>"; // reports: react/no-danger (error) const a = <div dangerouslySetInnerHTML={{ __html: html }} />;

react/no-danger-with-children

Reject combining dangerouslySetInnerHTML with JSX children, React throws at runtime in this case.

Example:

const html = "<strong>trusted</strong>"; // reports: react/no-danger-with-children (error) const a = <div dangerouslySetInnerHTML={{ __html: html }}>extra</div>;

react/no-direct-mutation-state

Reject direct writes to this.state outside constructor initialization. Use this.setState(...) instead.

Example:

import { Component } from "react"; class Counter extends Component<unknown, { count: number }> { bump() { // reports: react/no-direct-mutation-state (error) this.state.count = 1; } }

react/no-find-dom-node

Reject findDOMNode(...) calls.

The API is deprecated, blocks future React internals work, and breaks component abstraction by reaching across composition boundaries; attach a ref to the target element instead.

Example:

const ReactDOM = { findDOMNode: (component: unknown) => component }; const component = {}; // reports: react/no-find-dom-node (error) const a = ReactDOM.findDOMNode(component);

react/no-is-mounted

Reject this.isMounted() calls on class components.

The API is deprecated, and anti-patterns around it usually hide a memory leak in async callbacks; cancel the work in componentWillUnmount instead.

Example:

class Widget extends Component { refresh() { // reports: react/no-is-mounted (error) if (this.isMounted()) { } } }

react/no-string-refs

Reject string-form JSX refs (ref="myInput").

The API is legacy, slated for removal, and has well-known issues with static typing, owner tracking, and stale references; use useRef or a callback ref instead.

Example:

// reports: react/no-string-refs (error) const a = <input ref="name" />;

react/no-unescaped-entities

Reject unescaped >, ", ', and } characters in JSX text content, they render literally and almost always come from forgotten HTML escaping.

Example:

// reports: react/no-unescaped-entities (error) const a = <p>She said "hello"</p>;

react/only-export-components

Keep React Fast Refresh component modules from exporting non-component values. Mixing a component export with a constant or hook in the same file invalidates HMR.

Options:

  • extraHOCs?: readonly string[]

    Extra higher-order component names that wrap component exports. Default: [ ].

  • allowExportNames?: readonly string[]

    Export names the active framework handles during refresh, such as route metadata exports. Default: [ ].

  • allowConstantExport?: boolean

    Permit literal / string / boolean / template / binary constant exports alongside component exports. Default: false.

  • checkJS?: boolean

    Also scan JavaScript files that import React. TSX files are always scanned. Default: false.

Example:

export const helper = 1; // reports: react/only-export-components (error) export function Widget() { return <div>hi</div>; }

react/refs

Reject reading or writing ref.current during render.

Refs persist mutably across renders without re-rendering, so touching them in render breaks the pure-render contract and produces inconsistent output between render passes.

Example:

function Component() { const ref = useRef<number>(0); // reports: react/refs (error) const value = ref.current; return <div>{value}</div>; }

react/rules-of-hooks

Enforce the Rules of Hooks: only call Hooks at the top level of a component or custom Hook, never inside conditionals, loops, or nested functions.

Example:

import { useState } from "react"; function Component({ enabled }: { enabled: boolean }) { if (enabled) { // reports: react/rules-of-hooks (error) const [value] = useState(0); } return null; }

react/set-state-in-effect

Reject unconditional setState calls in useEffect bodies.

The state update schedules another render and re-runs the effect, which usually loops forever; the value almost always belongs in useMemo or derived state instead.

Example:

import { useEffect, useState } from "react"; function Component() { const [value, setValue] = useState(0); useEffect(() => { // reports: react/set-state-in-effect (error) setValue(1); }, []); return <div>{value}</div>; }

react/set-state-in-render

Reject calling setState during render.

Render must stay pure; queuing an update from the render body schedules another render that queues another update, producing an infinite render loop unless guarded by an explicit equality check.

Example:

import { useState } from "react"; function Component() { const [value, setValue] = useState(0); // reports: react/set-state-in-render (error) setValue(1); return <div>{value}</div>; }

react/style-prop-object

Reject string-form style values such as style="color: red" or style={\}.

React expects a { camelCaseProp: value } object, not the HTML-style CSS string, and the latter is silently coerced and never applied.

Example:

// reports: react/style-prop-object (error) const a = <div style="color: red" />;

react/use-memo

Reject useMemo calculation callbacks that do not return a value.

A block-bodied callback without return memoizes undefined, silently discarding the intended computation, a common mistake when wrapping an object literal in { ... } instead of ({ ... }).

Example:

import { useMemo } from "react"; function Component() { // reports: react/use-memo (error) const value = useMemo(() => ({ label: "Save" }), []); return <div>{value}</div>; }

react/void-dom-elements-no-children

Reject children or dangerouslySetInnerHTML on void DOM elements (<img>, <br>, <input>, etc.).

Void elements have no content model, so React throws at render time when you try to give them any.

Example:

// reports: react/void-dom-elements-no-children (error) const a = <img src="/x">caption</img>;
Last updated on