New
Introducing React Bench, see how different models perform on React code

Rules reference

802 active rules and 6 retired compatibility entries, grouped by category. Each page explains when a finding applies, how to fix or review it, and whether it is enabled by default.

Design-tagged rules are disabled by default. Some are creative-direction reviews; others identify accessibility, behavior, compatibility, or performance risks. Each page states the evidence required and its assessment class. The tag alone never makes a concrete defect optional.

Run react-doctor rules list to see the effective severity for your project. Use rules explain, rules set, or rules disable to inspect or change one rule.

Active rules are shown by default. Use the filters to include retired compatibility entries or narrow the list by category, assessment, and default configuration.

Every rule is also fetchable as Markdown at /docs/rules/{plugin}/{rule}.md, for example react-doctor/no-derived-state.

Filter rules

802 rules

Accessibility

Show 93 rules

Architecture

Show 56 rules
  • react-doctor/design-no-em-dash-in-jsx-textCreative-direction reviewDisabled by default: Replace em dashes in JSX prose with commas, colons, semicolons, or parentheses so UI copy reads less like generated text.
  • react-doctor/design-no-redundant-padding-axesEvidence-required riskDisabled by default: Collapse `px-N py-N` to `p-N` when both axes match. Keep them split only when one axis varies at a breakpoint (`py-2 md:py-3`)
  • react-doctor/design-no-redundant-size-axesEvidence-required riskDisabled by default: Collapse `w-N h-N` to `size-N` (Tailwind v3.4+) when both axes match
  • react-doctor/design-no-space-on-flex-childrenEvidence-required riskDisabled by default: Use `gap-*` on the flex/grid parent. `space-x-*` / `space-y-*` produce phantom gaps when a sibling is conditionally rendered, lose vertical spacing on wrapped lines, and don't mirror in RTL
  • react-doctor/design-no-three-period-ellipsisCreative-direction reviewDisabled by default: Use the typographic ellipsis "…" (or `…`) instead of three periods: pairs with action-with-followup labels ("Rename…", "Loading…")
  • react-doctor/display-nameEvidence-required riskDisabled by default: Give each component a stable displayName so React DevTools shows a real name instead of "Unknown".
  • react-doctor/forbid-component-propsEvidence-required riskDisabled by default: Configure forbidden props per component via the `forbidComponentProps.forbid` setting.
  • react-doctor/forbid-dom-propsEvidence-required risk: Configure forbidden DOM props via the `forbidDomProps.forbid` setting to keep disallowed attributes off DOM nodes.
  • react-doctor/forbid-elementsEvidence-required risk: Replace each configured forbidden element with its sanctioned component or wrapper.
  • react-doctor/forward-ref-uses-refEvidence-required risk: Either accept a `ref` parameter in the forwardRef render function, or drop the forwardRef wrapper entirely.
  • react-doctor/hook-use-stateEvidence-required riskDisabled by default: Destructure useState as `const [thing, setThing] = useState(…)`.
  • react-doctor/jsx-boolean-valueEvidence-required riskDisabled by default: Pick one boolean-attribute style codebase-wide (default: omit `={true}`, e.g. write `<C foo />`).
  • react-doctor/jsx-curly-brace-presenceEvidence-required riskDisabled by default: Pick a consistent quoting style for JSX literal values and drop redundant curly braces around plain strings.
  • react-doctor/jsx-filename-extensionEvidence-required riskDisabled by default: Use .jsx / .tsx (or your project's chosen extension) for files containing JSX.
  • react-doctor/jsx-fragmentsEvidence-required riskDisabled by default: Pick one fragment style across the codebase: use the <></> shorthand by default.
  • react-doctor/jsx-handler-namesEvidence-required riskDisabled by default: Use the `on…` prefix for event-handler props and `handle…` for the functions that handle them.
  • react-doctor/jsx-max-depthEvidence-required risk: Extract deeply nested JSX into smaller components to keep render trees readable.
  • react-doctor/jsx-no-useless-fragmentEvidence-required riskDisabled by default: Drop the fragment when it wraps a single child or holds multiple children directly under an HTML tag.
  • react-doctor/jsx-pascal-caseEvidence-required riskDisabled by default: Rename custom JSX components to PascalCase.
  • react-doctor/jsx-props-no-spreadingEvidence-required riskDisabled by default: List each prop explicitly so consumers can see what's being passed instead of spreading.
  • react-doctor/no-clone-elementEvidence-required riskDisabled by default: Pass children, render props, or Children.map instead of cloning elements with React.cloneElement.
  • react-doctor/no-dark-mode-glowCreative-direction reviewDisabled by default: Use a subtle `box-shadow` with neutral colors for depth, or `border` with low opacity. Colored glows on dark backgrounds are the default AI-generated aesthetic
  • react-doctor/no-default-propsEvidence-required risk: React 19 removes `Component.defaultProps` for function components. Move the defaults into the destructured props parameter: `function Foo({ size = "md", variant = "primary" })` instead of `Foo.defaultProps = { size: "md", variant: "primary" }`.
  • react-doctor/no-generic-handler-namesEvidence-required riskDisabled by default: Rename to describe the action: e.g. `handleSubmit` → `saveUserProfile`, `handleClick` → `toggleSidebar`
  • react-doctor/no-giant-componentEvidence-required risk: Extract logical sections into focused components: `<UserHeader />`, `<UserActions />`, etc.
  • react-doctor/no-gradient-textCreative-direction reviewDisabled by default: Use solid text colors for readability. If you need emphasis, use font weight, size, or a distinct color instead of gradients
  • react-doctor/no-inline-exhaustive-styleEvidence-required riskDisabled by default: Move styles to a CSS class, CSS module, Tailwind utilities, or a styled component: inline objects with many properties hurt readability and create new references every render
  • react-doctor/no-many-boolean-propsEvidence-required risk: Split into compound components or named variants: `<Button.Primary />`, `<DialogConfirm />` instead of stacking `isPrimary`, `isConfirm` flags
  • react-doctor/no-multi-compEvidence-required risk: Move secondary components into their own files.
  • react-doctor/no-polymorphic-childrenEvidence-required risk: Expose explicit subcomponents (`<Button.Text>`, `<Button.Icon>`) so consumers don't need to switch on `typeof children`
  • react-doctor/no-prop-typesEvidence-required riskDisabled by default: Move propTypes to TypeScript types: `type Props = { value: number }; function Component(props: Props)`: React 19 ignores runtime propTypes
  • react-doctor/no-pure-black-backgroundCreative-direction reviewDisabled by default: Tint the background slightly toward your brand hue: e.g. `#0a0a0f` or Tailwind's `bg-gray-950`. Pure black looks harsh on modern displays
  • react-doctor/no-react-childrenEvidence-required riskDisabled by default: Pass children as props or render them directly instead of calling React.Children methods.
  • react-doctor/no-react-dom-deprecated-apisEvidence-required risk: On React 18+, migrate legacy root APIs to react-dom/client; migrate act from react-dom/test-utils to react only on React 19+
  • react-doctor/no-react19-deprecated-apisEvidence-required risk: Review React 19 ref-as-prop migrations; keep useContext unless a conditional context read specifically benefits from use(Context)
  • react-doctor/no-redundant-should-component-updateEvidence-required risk: Review shouldComponentUpdate overrides on PureComponent; deleting an override changes behavior unless it is proven equivalent to the inherited shallow comparison
  • react-doctor/no-render-in-renderEvidence-required risk: A render helper is reconciled normally; extract a component only when it needs an independent identity, state, or reusable boundary
  • react-doctor/no-render-prop-childrenEvidence-required risk: Replace `renderXxx` props with compound subcomponents (e.g. `<Modal.Header>`) or `children` so the parent doesn't dictate every customization point
  • react-doctor/no-set-stateEvidence-required riskDisabled by default: Lift state up or use an external store instead of this.setState.
  • react-doctor/no-side-tab-borderCreative-direction reviewDisabled by default: Use a subtler accent (box-shadow inset, background gradient, or border-bottom) instead of a thick one-sided border
  • react-doctor/no-unescaped-entitiesEvidence-required riskDisabled by default: Replace bare ' / " / > / } characters in JSX text with HTML entities.
  • react-doctor/no-wide-letter-spacingEvidence-required riskDisabled by default: Reserve wide tracking (letter-spacing > 0.05em) for short uppercase labels, navigation items, and buttons: not body text
  • react-doctor/no-z-index-9999Evidence-required riskDisabled by default: Define a z-index scale in your design tokens (e.g. dropdown: 10, modal: 20, toast: 30). Create a new stacking context with `isolation: isolate` instead of escalating values
  • react-doctor/only-export-componentsEvidence-required risk: Move non-component exports out of files that export components.
  • react-doctor/prefer-es6-classEvidence-required riskDisabled by default: Use one component style consistently: ES2015 `class extends React.Component` (default) over the legacy `createReactClass` factory.
  • react-doctor/prefer-explicit-variantsEvidence-required risk: Split into explicit variant components: render `<ThreadComposer />` and `<EditMessageComposer />` instead of one component switching subtrees on boolean props.
  • react-doctor/prefer-function-componentEvidence-required riskDisabled by default: Re-write the class component as a function component using hooks.
  • react-doctor/prefer-module-scope-pure-functionEvidence-required risk: Hoist the pure helper to module scope (above the component) so it isn't reallocated each render: const formatName = (user) => ...
  • react-doctor/prefer-module-scope-static-valueEvidence-required risk: Hoist the static array/object literal to module scope above the component: const FILTER_OPTIONS = ["all", "active", "done"]; function App() { ... }
  • react-doctor/react-compiler-no-manual-memoizationEvidence-required risk: Do not remove existing manual memoization solely because React Compiler is enabled; preserve it unless focused tests prove removal is safe
  • react-doctor/self-closing-compEvidence-required riskDisabled by default: Use the self-closing form `<X />` for elements with no children.
  • react-doctor/state-in-constructorEvidence-required riskDisabled by default: Pick one state-initialization style for class components: class field or constructor: and use it consistently.
  • react-doctor/zod-v4-no-deprecated-error-apisEvidence-required risk: Replace deprecated ZodError helpers with Zod 4 functions: z.treeifyError(), z.flattenError(), z.prettifyError(), or read error.issues directly
  • react-doctor/zod-v4-no-deprecated-error-customizationEvidence-required risk: Replace deprecated Zod error customization with the v4 unified { error } API: z.string({ error: "Required" })
  • react-doctor/zod-v4-no-deprecated-schema-apisEvidence-required risk: Migrate deprecated Zod 4 schema APIs: z.object().strict() to z.strictObject(), z.nativeEnum to z.enum, z.record(value) to z.record(key, value), z.function().args().returns() to z.function({ input, output }).
  • react-doctor/zod-v4-prefer-top-level-string-formatsEvidence-required risk: Replace z.string().<format>() with the Zod 4 top-level format API, e.g. z.email() or z.uuid()

Bugs

Show 219 rules

Bundle Size

Show 7 rules

Correctness

Show 46 rules

Design

Show 5 rules
  • react-doctor/no-ease-in-motionCreative-direction reviewDisabled by default: Review ease-in timing in its actual motion phase; it can delay user-visible response but remains valid for intentional exits
  • react-doctor/no-excessive-motion-staggerCreative-direction reviewDisabled by default: Review stagger spacing above 80ms in the context of item count, total tail, frequency, and product intent
  • react-doctor/no-inline-bounce-easingCreative-direction reviewDisabled by default: Review overshoot and bounce against the product motion system instead of treating one curve as universal
  • react-doctor/no-long-transition-durationCreative-direction reviewDisabled by default: Review UI transitions longer than one second; duration is primarily a responsiveness and attention concern, not proof of a rendering bottleneck
  • react-doctor/no-scale-from-zeroCreative-direction review: Review scale-to-zero entrances and exits; ordinary product UI usually reads more smoothly from a small nonzero scale plus opacity

Maintainability

Show 78 rules

Next.js

Show 22 rules

Performance

Show 109 rules

Preact

Show 5 rules

React Compiler

Show 15 rules
  • react-hooks-js/error-boundariesEvidence-required risk: Validates usage of error boundaries instead of try/catch for errors in child components
  • react-hooks-js/globalsEvidence-required risk: Validates against assignment/mutation of globals during render, part of ensuring that [side effects must render outside of render](https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
  • react-hooks-js/hooksEvidence-required risk: Validates the rules of hooks
  • react-hooks-js/immutabilityEvidence-required risk: Validates against mutating props, state, and other values that [are immutable](https://react.dev/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable)
  • react-hooks-js/incompatible-libraryEvidence-required risk: Validates against usage of libraries which are incompatible with memoization (manual or automatic)
  • react-hooks-js/preserve-manual-memoizationEvidence-required risk: Validate that React Compiler can preserve or exceed the observable guarantees of existing manual memoization
  • react-hooks-js/purityEvidence-required risk: Validates that [components/hooks are pure](https://react.dev/reference/rules/components-and-hooks-must-be-pure) by checking that they do not call known-impure functions
  • react-hooks-js/refsEvidence-required risk: Validates correct usage of refs, not reading/writing during render. See the "pitfalls" section in [`useRef()` usage](https://react.dev/reference/react/useRef#usage)
  • react-hooks-js/set-state-in-effectEvidence-required risk: Validates against calling setState synchronously in an effect. This can indicate non-local derived data, a derived event pattern, or improper external data synchronization.
  • react-hooks-js/set-state-in-renderEvidence-required risk: Validates against setting state during render, which can trigger additional renders and potential infinite render loops
  • react-hooks-js/static-componentsEvidence-required risk: Validates that components are static, not recreated every render. Components that are recreated dynamically can reset state and trigger excessive re-rendering
  • react-hooks-js/todoEvidence-required risk: Unimplemented features
  • react-hooks-js/unsupported-syntaxEvidence-required risk: Validates against syntax that we do not plan to support in React Compiler
  • react-hooks-js/use-memoEvidence-required risk: Validates usage of the useMemo() hook against common mistakes. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.
  • react-hooks-js/void-use-memoEvidence-required risk: Validates that useMemos always return a value and that the result of the useMemo is used by the component/hook. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.

React Native

Show 33 rules

Security

Show 56 rules
  • react-doctor/active-static-assetEvidence-required risk: A browser-reachable SVG that contains a `<script>` tag or `on*` event handler runs that code in your origin when someone opens it, which can lead to cross-site scripting.
  • react-doctor/agent-tool-capability-riskEvidence-required risk: An AI agent tool that can reach shell, filesystem, or network primitives lets prompt-injected input trigger those actions, because the model treats tool arguments as trusted.
  • react-doctor/artifact-baas-authority-surfaceEvidence-required risk: Shipping Firebase/Supabase client config with your collection and authorization-field names in a browser bundle hands attackers a map of your data model, which is dangerous when server-side rules do not enforce access.
  • react-doctor/artifact-env-leakEvidence-required risk: A real secret shipped in a browser bundle under a public env prefix (`NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`, `EXPO_PUBLIC_`) is world-readable and must be treated as compromised.
  • react-doctor/artifact-secret-leakEvidence-required risk: A live credential (API key, token, or connection string) sits in a browser bundle or static asset, so anyone can read it, and it must be treated as compromised.
  • react-doctor/auth-token-in-web-storageEvidence-required risk: Store authentication credentials in server-set `HttpOnly` cookies instead of browser storage, where cross-site scripting can read them.
  • react-doctor/build-pipeline-secret-boundaryEvidence-required risk: Installing dependencies while CI secrets are in the environment lets a malicious package's lifecycle script read those secrets, which risks supply-chain compromise.
  • react-doctor/clickjacking-redirect-riskEvidence-required risk: A redirect target taken from caller input, or a privileged page that allows untrusted framing, lets attackers send users to malicious sites or trick them through clickjacking.
  • react-doctor/command-execution-input-riskEvidence-required risk: Passing caller-controlled input into a shell command lets an attacker run arbitrary commands on your server (remote code execution).
  • react-doctor/cors-cookie-trust-riskEvidence-required risk: Combining credentialed CORS with a wildcard or less-trusted origin, or scoping auth cookies to a parent domain, lets other sites or subdomains ride a user's session.
  • react-doctor/dangerous-html-sinkEvidence-required risk: Passing user- or request-derived data into an HTML sink like `dangerouslySetInnerHTML` or `innerHTML` without sanitizing it allows cross-site scripting.
  • react-doctor/firebase-client-owned-authz-fieldEvidence-required risk: When the client writes ownership or role fields (`ownerId`, `orgId`, `role`, `isAdmin`) to Firebase/Supabase, an attacker can forge them and grant themselves access.
  • react-doctor/firebase-permissive-rulesEvidence-required risk: A Firebase rule of `if true` or `if request.auth != null` leaves data open to everyone (or to every signed-in user), treating sign-in as authorization and exposing other users' data.
  • react-doctor/firebase-query-filter-as-authEvidence-required risk: Relying on a client-side Firestore `.where('userId', '==', …)` filter for access control is unsafe, because a client can drop the filter and read everyone's data.
  • react-doctor/git-provider-url-injection-riskEvidence-required risk: Interpolating request input into a Git provider URL without encoding lets an attacker inject extra path segments or parameters and redirect the request.
  • react-doctor/iframe-missing-sandboxEvidence-required risk: Add sandbox="" (or a curated, minimal set of allow- tokens) to your iframe to restrict embedded content.
  • react-doctor/import-metadata-execution-riskEvidence-required risk: Evaluating imported metadata or file contents (EXIF, manifests, presets, uploads, archives) as code lets an attacker achieve remote code execution.
  • react-doctor/insecure-crypto-riskEvidence-required risk: Weak primitives (MD5, SHA-1, DES, RC4), non-timing-safe comparisons, or `Math.random()` for security values make signatures, tokens, and passwords easier to forge or guess.
  • react-doctor/insecure-session-cookieEvidence-required risk: Auth cookie missing HttpOnly protection
  • react-doctor/jsx-no-target-blankEvidence-required risk: Add rel="noreferrer" (or "noopener") whenever using target="_blank".
  • react-doctor/jwt-insecure-verificationEvidence-required risk: JWT verified with the 'none' algorithm
  • react-doctor/key-lifecycle-riskEvidence-required risk: A private key or release credential committed inline to the repo is exposed in git history and must be rotated and revoked.
  • react-doctor/local-rpc-native-bridge-riskEvidence-required risk: A localhost or native bridge that accepts loose origins and exposes install/update or shell commands lets a malicious web page drive native actions on the user's machine.
  • react-doctor/mcp-tool-capability-riskEvidence-required risk: An MCP tool runs with the connecting client's authority, so reaching shell, filesystem, or network primitives without validation lets injected input abuse them.
  • react-doctor/mdx-ssr-execution-riskEvidence-required risk: Compiling untrusted MDX with the full pipeline runs attacker-supplied JSX and expressions on your server, which can lead to code execution.
  • react-doctor/nextjs-no-side-effect-in-get-handlerEvidence-required risk: Move the side effect to a POST handler and use a <form> or fetch with method POST: GET requests can be triggered by prefetching and are vulnerable to CSRF
  • react-doctor/no-evalEvidence-required risk: Use `JSON.parse` for serialized data, `Function(...)` (still careful) for trusted templates, or refactor to avoid dynamic code execution
  • react-doctor/no-path-prefix-containmentEvidence-required risk: Path containment check uses a string prefix
  • react-doctor/no-secrets-in-client-codeEvidence-required risk: Move secrets to server-only code. Public client environment variables are bundled into browser code and must not contain secrets
  • react-doctor/nosql-injection-riskEvidence-required risk: Building a NoSQL query from raw client input lets an attacker inject operator-shaped keys or `$where` code and read or alter data they should not.
  • react-doctor/package-metadata-secretEvidence-required risk: A secret or public-prefixed secret name in `package.json` leaks easily, because package metadata is routinely published to registries, logs, and browser bundles.
  • react-doctor/path-traversal-riskEvidence-required risk: Building a filesystem path from request input lets an attacker use `..` or absolute paths to read or write files outside the intended directory.
  • react-doctor/plugin-update-trust-riskEvidence-required risk: Downloading and running an update or plugin without verifying its integrity lets an attacker ship malicious code to your users.
  • react-doctor/postmessage-origin-riskEvidence-required risk: Reading `event.data` in a `message` handler without checking `event.origin` lets any other window send data your code trusts, which can lead to cross-site scripting or data theft.
  • react-doctor/public-debug-artifactEvidence-required risk: A browser-reachable debug, log, dump, or report file in your build output can expose source paths, internal routes, env data, or secrets.
  • react-doctor/public-env-secret-nameEvidence-required risk: A public-prefixed env var whose name implies a secret (token, password, private key, service role) is inlined into the client bundle, so a real credential there is world-readable.
  • react-doctor/raw-sql-injection-riskEvidence-required risk: Building a SQL query by string concatenation or an unsafe raw helper lets an attacker inject SQL and read or modify your database.
  • react-doctor/react-markdown-unsanitized-raw-htmlEvidence-required risk: Unsanitized raw HTML in React Markdown
  • react-doctor/react-router-csp-nonce-consistencyEvidence-required risk: CSP nonce is not shared across server rendering
  • react-doctor/repository-secret-fileEvidence-required risk: A committed env file, credential, or token is exposed to anyone with repo access and must be rotated, even after you remove it.
  • react-doctor/request-body-mass-assignmentEvidence-required risk: Request input spread without field allowlist
  • react-doctor/require-pnpm-hardeningEvidence-required risk: pnpm project is missing supply-chain hardening in pnpm-workspace.yaml: set `minimumReleaseAge`, keep `blockExoticSubdeps: true`, and set `trustPolicy: no-downgrade`
  • react-doctor/secret-in-fallbackEvidence-required risk: Hardcoded secret fallback for env var
  • react-doctor/supabase-client-owned-authz-fieldEvidence-required risk: When the client writes authorization columns (`ownerId`, `orgId`, `role`, `isAdmin`) to Supabase, an attacker can forge them and escalate their own access.
  • react-doctor/supabase-rls-policy-riskEvidence-required risk: A Supabase policy that disables row-level security, exposes the service role, or uses a `(true)` write predicate lets clients read or modify data that is not theirs.
  • react-doctor/supabase-table-missing-rlsEvidence-required risk: Supabase table created without Row Level Security
  • react-doctor/svg-filter-clickjacking-riskEvidence-required risk: Applying CSS or SVG filters over a cross-origin iframe can be used for clickjacking or to read pixels from framed content the attacker should not see.
  • react-doctor/tanstack-start-get-mutationEvidence-required risk: Use `createServerFn({ method: 'POST' })` for data modifications: GET requests can be triggered by prefetching and are vulnerable to CSRF
  • react-doctor/tanstack-start-no-secrets-in-loaderEvidence-required risk: Loaders are isomorphic (run on both server and client). Wrap secret access in `createServerFn()` so it stays server-only
  • react-doctor/tenant-static-proxy-riskEvidence-required risk: Building an asset path from a client-supplied tenant, subdomain, or workspace value lets one tenant read another tenant's files.
  • react-doctor/unsafe-json-in-htmlEvidence-required risk: Unescaped JSON in HTML or script sink
  • react-doctor/untrusted-redirect-followingEvidence-required risk: Following a redirect from a request-supplied URL without re-validating each hop lets an attacker bounce your server into internal addresses (server-side request forgery).
  • react-doctor/url-prefilled-privileged-actionEvidence-required risk: Reading a privileged action from the URL (invite, role, permission, redirect, sharing) and acting on it lets an attacker craft a link that performs that action for a victim.
  • react-doctor/webhook-signature-riskEvidence-required risk: An inbound webhook handler that acts on the request body without verifying the provider's signature will process forged requests from anyone.
  • react-doctor/window-open-without-noopenerEvidence-required risk: window.open without noopener
  • socket/low-supply-chain-scoreEvidence-required risk: A direct dependency's worst Socket security axis (supply chain or vulnerability) scores below the configured minimum: bump it to a patched/healthier release, replace it, or vet it and raise `supplyChain.minScore`

Server

Show 8 rules

State & Effects

Show 32 rules
  • react-doctor/activity-wraps-effect-heavy-subtreeEvidence-required risk: Audit whether Effects inside a toggleable Activity should pause while hidden; Activity preserves DOM and state while cleaning up and later recreating Effects
  • react-doctor/effect-needs-cleanupEvidence-required risk: Return a cleanup function that releases the subscription / timer: `return () => target.removeEventListener(name, handler)` for listeners, `return () => clearInterval(id)` / `clearTimeout(id)` for timers, or `return unsubscribe` if the subscribe call already returned one
  • react-doctor/hooks-no-nan-in-depsEvidence-required risk: Review a literal NaN dependency as a likely placeholder; Object.is treats stable NaN as unchanged and detects NaN-to-number transitions
  • react-doctor/jotai-derived-atom-returns-fresh-objectEvidence-required risk: Split the derivation into per-field primitive derived atoms, or wrap with selectAtom(source, fn, shallow) from jotai/utils when a wrapper object is required.
  • react-doctor/jotai-select-atom-in-render-bodyEvidence-required risk: Lift selectAtom to module scope, or wrap it: const a = useMemo(() => selectAtom(base, fn), [deps])
  • react-doctor/jotai-tq-use-raw-query-atomEvidence-required risk: Derive the field once, then subscribe to the derived atom: const dataAtom = atom((get) => get(queryAtom).data)
  • react-doctor/no-derived-stateEvidence-required risk: Disallow storing derived state in an effect.
  • react-doctor/no-derived-state-effectEvidence-required risk: For derived state, compute inline: `const x = fn(dep)`. For state resets on prop change, use a key prop: `<Component key={prop} />`. See https://react.dev/learn/you-might-not-need-an-effect
  • react-doctor/no-derived-useStateEvidence-required risk: Remove useState and compute the value inline: `const value = transform(propName)`
  • react-doctor/no-did-mount-set-stateEvidence-required risk: Derive state in getDerivedStateFromProps or initial state instead of calling this.setState in componentDidMount, which forces an extra render.
  • react-doctor/no-did-update-set-stateEvidence-required risk: Avoid calling this.setState in componentDidUpdate; derive the value with getDerivedStateFromProps to prevent re-render loops
  • react-doctor/no-direct-state-mutationEvidence-required risk: Replace the mutation with a setter call that produces a new reference: `setItems([...items, newItem])`, `setItems(items.filter(x => x !== target))`, `setItems(items.toSorted(...))`. React only re-renders on a new reference, so in-place updates are silently dropped
  • react-doctor/no-effect-chainEvidence-required risk: Compute as much as possible during render (e.g. `const isGameOver = round > 5`) and write all related state inside the event handler that originally fires the chain. Each effect link adds an extra render and makes the code rigid as requirements evolve
  • react-doctor/no-effect-event-handlerEvidence-required risk: Move the conditional logic into onClick, onChange, or onSubmit handlers directly
  • react-doctor/no-effect-event-in-depsEvidence-required risk: Call the useEffectEvent callback inside the effect body without listing it; its identity is intentionally unstable
  • react-doctor/no-effect-with-fresh-depsEvidence-required risk: Move the constructed value into the hook body and depend on its primitive inputs, or memoize it with useMemo/useCallback so its reference is stable.
  • react-doctor/no-event-handlerEvidence-required risk: Disallow using state and an effect as an event handler.
  • react-doctor/no-event-trigger-stateEvidence-required risk: Delete the trigger state (`useState(null)` plus the `useEffect` that watches it) and call the side-effect (`post(...)` / `navigate(...)` / `track(...)`) directly inside the event handler that previously called the setter. State should not exist purely to schedule effect runs
  • react-doctor/no-fetch-in-effectEvidence-required risk: Use `useQuery()` from @tanstack/react-query, `useSWR()`, or fetch in a Server Component instead
  • react-doctor/no-initialize-stateEvidence-required risk: Disallow initializing state in an effect.
  • react-doctor/no-mirror-prop-effectEvidence-required risk: Delete both the `useState` and the `useEffect` and read the prop directly during render. Mirroring a prop into local state forces a stale first render before the effect re-syncs
  • react-doctor/no-mutable-in-depsEvidence-required risk: Read mutable values (`location.pathname`, `ref.current`) inside the effect body instead of in the deps array, or subscribe with `useSyncExternalStore`. Mutations to these don't trigger re-renders, so listing them in deps doesn't make the effect react to changes
  • react-doctor/no-mutating-reducer-stateEvidence-required risk: Return a new reducer state object/array/collection instead of mutating the current state and returning the same top-level reference.
  • react-doctor/no-prop-callback-in-effectEvidence-required risk: Lift shared state to the nearest common owner; use Context only when direct props would cross distant or numerous consumers
  • react-doctor/no-reset-all-state-on-prop-changeEvidence-required risk: Disallow resetting all state in an effect when a prop changes.
  • react-doctor/no-self-updating-effectEvidence-required risk: Break the self-updating-effect feedback loop: derive the value during render, move the write into an event handler, or guard the update so it provably converges.
  • react-doctor/no-set-state-in-renderEvidence-required risk: Move the setter call into a `useEffect`, an event handler, or replace the state with a value computed during render. Calling a setter at render time triggers another render, which calls the setter again: an infinite loop
  • react-doctor/no-will-update-set-stateEvidence-required risk: Don't call this.setState in componentWillUpdate: move the update to getDerivedStateFromProps or componentDidUpdate.
  • react-doctor/prefer-use-effect-eventEvidence-required risk: On React 19.2+, use useEffectEvent for non-reactive callbacks called only from an Effect
  • react-doctor/prefer-use-sync-external-storeEvidence-required risk: Replace a manual external-store subscription with useSyncExternalStore while preserving subscribe receiver semantics and stable snapshots
  • react-doctor/prefer-useReducerEvidence-required risk: Group related state: `const [state, dispatch] = useReducer(reducer, { field1, field2, ... })`
  • react-doctor/rerender-dependenciesEvidence-required risk: Extract to a useMemo, useRef, or module-level constant so the reference is stable

TanStack Query

Show 7 rules

TanStack Start

Show 11 rules