# React Doctor rule prompts

802 active rules and 6 retired compatibility entries, grouped by category. Each rule states its proof boundary, required evidence, and fix or review path.

Design-tagged rules are disabled by default. Some are creative-direction reviews; others identify accessibility, behavior, compatibility, or performance risks. The tag alone never makes a concrete defect optional.

Fetch any rule as Markdown at `/prompts/rules/{plugin}/{rule}.md`. For example: `/prompts/rules/react-doctor/no-derived-state.md`.

## Accessibility

- [`react-doctor/alt-text`](/prompts/rules/react-doctor/alt-text.md): Require alternative text on img, area, input[type='image'], and object elements so screen readers can describe them.
- [`react-doctor/anchor-ambiguous-text`](/prompts/rules/react-doctor/anchor-ambiguous-text.md): Describe a link's destination: avoid bare 'click here' / 'learn more' / 'link' as the only link text.
- [`react-doctor/anchor-has-content`](/prompts/rules/react-doctor/anchor-has-content.md): Add visible or aria-labelled text inside every `<a>`.
- [`react-doctor/anchor-is-valid`](/prompts/rules/react-doctor/anchor-is-valid.md): Require anchors to navigate to a real destination: use a button for click-only actions instead of href='#' or javascript:void(0).
- [`react-doctor/aria-activedescendant-has-tabindex`](/prompts/rules/react-doctor/aria-activedescendant-has-tabindex.md): Add `tabIndex` to elements with `aria-activedescendant` so they're keyboard-focusable.
- [`react-doctor/aria-braille-equivalent`](/prompts/rules/react-doctor/aria-braille-equivalent.md): Braille-only accessible description
- [`react-doctor/aria-props`](/prompts/rules/react-doctor/aria-props.md): Use only documented aria-* attributes from the WAI-ARIA spec.
- [`react-doctor/aria-proptypes`](/prompts/rules/react-doctor/aria-proptypes.md): Give each aria-* attribute a value matching its WAI-ARIA type (boolean, token, integer, ID, ID-list, etc.).
- [`react-doctor/aria-role`](/prompts/rules/react-doctor/aria-role.md): Use a documented, non-abstract WAI-ARIA role for every role attribute.
- [`react-doctor/aria-unsupported-elements`](/prompts/rules/react-doctor/aria-unsupported-elements.md): Don't put role / aria-* attributes on reserved HTML elements like meta, head, script, or style.
- [`react-doctor/autocomplete-valid`](/prompts/rules/react-doctor/autocomplete-valid.md): Use a valid HTML autofill token in autoComplete.
- [`react-doctor/click-events-have-key-events`](/prompts/rules/react-doctor/click-events-have-key-events.md): Require a keyboard handler alongside onClick on non-interactive elements so keyboard users are not locked out.
- [`react-doctor/control-has-associated-label`](/prompts/rules/react-doctor/control-has-associated-label.md): Give every interactive control an accessible label via visible text, aria-label, aria-labelledby, or an associated label.
- [`react-doctor/data-table-requires-accessible-name`](/prompts/rules/react-doctor/data-table-requires-accessible-name.md): Data table has no accessible name
- [`react-doctor/design-no-vague-button-label`](/prompts/rules/react-doctor/design-no-vague-button-label.md): Name the action: "Save changes" instead of "Continue", "Send invite" instead of "Submit", "Delete account" instead of "OK". The label IS the button's accessible name
- [`react-doctor/details-requires-summary`](/prompts/rules/react-doctor/details-requires-summary.md): Details disclosure has no explicit summary
- [`react-doctor/dialog-has-accessible-name`](/prompts/rules/react-doctor/dialog-has-accessible-name.md): Dialog without accessible name
- [`react-doctor/empty-table-header`](/prompts/rules/react-doctor/empty-table-header.md): Table header has no accessible text
- [`react-doctor/fieldset-requires-legend`](/prompts/rules/react-doctor/fieldset-requires-legend.md): Fieldset group has no legend
- [`react-doctor/heading-has-content`](/prompts/rules/react-doctor/heading-has-content.md): Disallow empty headings: h1 through h6 must render accessible text content.
- [`react-doctor/html-has-lang`](/prompts/rules/react-doctor/html-has-lang.md): Require a lang attribute on the html element so screen readers use the right pronunciation rules.
- [`react-doctor/html-xml-lang-mismatch`](/prompts/rules/react-doctor/html-xml-lang-mismatch.md): Conflicting document language declarations
- [`react-doctor/iframe-has-title`](/prompts/rules/react-doctor/iframe-has-title.md): Require a unique, descriptive title attribute on iframes so assistive-tech users can identify embedded content.
- [`react-doctor/iframe-title-unique`](/prompts/rules/react-doctor/iframe-title-unique.md): Frame title is duplicated
- [`react-doctor/img-redundant-alt`](/prompts/rules/react-doctor/img-redundant-alt.md): Drop redundant words like 'image' / 'photo' / 'picture' from alt text and describe the content instead.
- [`react-doctor/ink-valid-aria-semantics`](/prompts/rules/react-doctor/ink-valid-aria-semantics.md): Invalid Ink accessibility semantics
- [`react-doctor/interactive-supports-focus`](/prompts/rules/react-doctor/interactive-supports-focus.md): Add tabIndex to elements that have interactive roles and event handlers.
- [`react-doctor/label-has-associated-control`](/prompts/rules/react-doctor/label-has-associated-control.md): Require every label to wrap its form control or point at one with htmlFor.
- [`react-doctor/lang`](/prompts/rules/react-doctor/lang.md): Use a valid BCP-47 language tag on `<html lang>` (e.g. `en` / `en-US`).
- [`react-doctor/loading-action-preserves-trigger`](/prompts/rules/react-doctor/loading-action-preserves-trigger.md): Loading state removes its initiating action
- [`react-doctor/media-has-caption`](/prompts/rules/react-doctor/media-has-caption.md): Add a `<track kind="captions">` child to every `<audio>` / `<video>`.
- [`react-doctor/mouse-events-have-key-events`](/prompts/rules/react-doctor/mouse-events-have-key-events.md): Pair mouse events with their keyboard equivalents.
- [`react-doctor/no-access-key`](/prompts/rules/react-doctor/no-access-key.md): Don't use `accessKey`: it conflicts with assistive-technology shortcuts.
- [`react-doctor/no-all-caps-body-text`](/prompts/rules/react-doctor/no-all-caps-body-text.md): Long body copy is set in all caps
- [`react-doctor/no-arbitrary-px-font-size`](/prompts/rules/react-doctor/no-arbitrary-px-font-size.md): Pixel arbitrary font size
- [`react-doctor/no-aria-hidden-on-body`](/prompts/rules/react-doctor/no-aria-hidden-on-body.md): Document body hidden from assistive technology
- [`react-doctor/no-aria-hidden-on-focusable`](/prompts/rules/react-doctor/no-aria-hidden-on-focusable.md): Remove `aria-hidden` from focusable elements (or remove the focusability).
- [`react-doctor/no-aria-invalid-without-description`](/prompts/rules/react-doctor/no-aria-invalid-without-description.md): Invalid control has no error description
- [`react-doctor/no-assertive-status`](/prompts/rules/react-doctor/no-assertive-status.md): Status live region interrupts assistive technology
- [`react-doctor/no-autofocus`](/prompts/rules/react-doctor/no-autofocus.md): Disallow the autoFocus prop: stolen focus disorients screen-reader and keyboard users.
- [`react-doctor/no-autoplay-without-muted`](/prompts/rules/react-doctor/no-autoplay-without-muted.md): Autoplaying media without muted
- [`react-doctor/no-blocked-paste`](/prompts/rules/react-doctor/no-blocked-paste.md): Paste blocked in an authentication field
- [`react-doctor/no-cramped-container-padding`](/prompts/rules/react-doctor/no-cramped-container-padding.md): Bounded text container has cramped padding
- [`react-doctor/no-crushed-letter-spacing`](/prompts/rules/react-doctor/no-crushed-letter-spacing.md): Letter spacing compresses text excessively
- [`react-doctor/no-disabled-zoom`](/prompts/rules/react-doctor/no-disabled-zoom.md): Remove `user-scalable=no` and `maximum-scale` from the viewport meta tag. If your layout breaks at 200% zoom, fix the layout: don't punish users with disabilities
- [`react-doctor/no-distracting-elements`](/prompts/rules/react-doctor/no-distracting-elements.md): Disallow deprecated marquee and blink elements, which distract users and can trigger vestibular issues.
- [`react-doctor/no-duplicate-static-id-reference`](/prompts/rules/react-doctor/no-duplicate-static-id-reference.md): Referenced ID is duplicated
- [`react-doctor/no-focus-in-animation-completion-handler`](/prompts/rules/react-doctor/no-focus-in-animation-completion-handler.md): Focus waits for animation completion
- [`react-doctor/no-focusable-content-in-aria-hidden`](/prompts/rules/react-doctor/no-focusable-content-in-aria-hidden.md): aria-hidden subtree contains focusable content
- [`react-doctor/no-focusable-content-in-role-text`](/prompts/rules/react-doctor/no-focusable-content-in-role-text.md): Focusable control inside role=text
- [`react-doctor/no-gray-on-colored-background`](/prompts/rules/react-doctor/no-gray-on-colored-background.md): Use a darker shade of the background color for text, or white/near-white for contrast. Gray text on colored backgrounds looks washed out
- [`react-doctor/no-hover-only-reveal`](/prompts/rules/react-doctor/no-hover-only-reveal.md): Content is revealed only on hover
- [`react-doctor/no-inert-pointer-affordance`](/prompts/rules/react-doctor/no-inert-pointer-affordance.md): Pointer cursor has no interaction
- [`react-doctor/no-interactive-element-to-noninteractive-role`](/prompts/rules/react-doctor/no-interactive-element-to-noninteractive-role.md): Don't override an interactive element's semantics with a non-interactive role.
- [`react-doctor/no-invalid-progress-range`](/prompts/rules/react-doctor/no-invalid-progress-range.md): Progress value falls outside its range
- [`react-doctor/no-invisible-focus-control`](/prompts/rules/react-doctor/no-invisible-focus-control.md): Invisible native control lacks keyboard focus treatment
- [`react-doctor/no-justified-text`](/prompts/rules/react-doctor/no-justified-text.md): Use `text-align: left` for body text, or add `hyphens: auto` and `overflow-wrap: break-word` if you must justify
- [`react-doctor/no-low-contrast-inline-style`](/prompts/rules/react-doctor/no-low-contrast-inline-style.md): Low-contrast text in inline style
- [`react-doctor/no-multiple-main-landmarks`](/prompts/rules/react-doctor/no-multiple-main-landmarks.md): View contains multiple main landmarks
- [`react-doctor/no-multiple-unlabeled-navigation-landmarks`](/prompts/rules/react-doctor/no-multiple-unlabeled-navigation-landmarks.md): Repeated navigation landmarks need unique names
- [`react-doctor/no-noninteractive-element-interactions`](/prompts/rules/react-doctor/no-noninteractive-element-interactions.md): Move the interaction to a semantic interactive element, or add an interactive role plus keyboard support.
- [`react-doctor/no-noninteractive-element-to-interactive-role`](/prompts/rules/react-doctor/no-noninteractive-element-to-interactive-role.md): Use a semantic interactive element instead of role-promoting a non-interactive one.
- [`react-doctor/no-noninteractive-tabindex`](/prompts/rules/react-doctor/no-noninteractive-tabindex.md): Reserve tabIndex for interactive elements or interactive roles; remove it from non-interactive ones.
- [`react-doctor/no-nonresizable-textarea`](/prompts/rules/react-doctor/no-nonresizable-textarea.md): Textarea resizing is disabled
- [`react-doctor/no-outline-none`](/prompts/rules/react-doctor/no-outline-none.md): Use `:focus-visible { outline: 2px solid var(--color-muted); outline-offset: 2px }` to show focus only for keyboard users while hiding it for mouse clicks
- [`react-doctor/no-overwide-text-measure`](/prompts/rules/react-doctor/no-overwide-text-measure.md): Text measure is too wide
- [`react-doctor/no-placeholder-only-field`](/prompts/rules/react-doctor/no-placeholder-only-field.md): Field relies on placeholder text for its label
- [`react-doctor/no-pointer-disabled-enabled-control`](/prompts/rules/react-doctor/no-pointer-disabled-enabled-control.md): Enabled control ignores pointer input
- [`react-doctor/no-presentation-role-conflict`](/prompts/rules/react-doctor/no-presentation-role-conflict.md): Presentational element exposes conflicting semantics
- [`react-doctor/no-reduced-motion-content-removal`](/prompts/rules/react-doctor/no-reduced-motion-content-removal.md): Reduced motion removes meaningful content
- [`react-doctor/no-redundant-roles`](/prompts/rules/react-doctor/no-redundant-roles.md): Disallow role attributes that duplicate an element's implicit ARIA role, like <button role='button'>.
- [`react-doctor/no-responsive-hidden-accessible-name`](/prompts/rules/react-doctor/no-responsive-hidden-accessible-name.md): Responsive styles hide a control's accessible name
- [`react-doctor/no-server-side-image-map`](/prompts/rules/react-doctor/no-server-side-image-map.md): Server-side image map
- [`react-doctor/no-skipped-heading-level`](/prompts/rules/react-doctor/no-skipped-heading-level.md): Heading hierarchy skips a level
- [`react-doctor/no-small-form-control-text`](/prompts/rules/react-doctor/no-small-form-control-text.md): Form control text is smaller than 16px
- [`react-doctor/no-smooth-scroll-without-reduced-motion`](/prompts/rules/react-doctor/no-smooth-scroll-without-reduced-motion.md): Smooth scrolling ignores reduced motion
- [`react-doctor/no-static-element-interactions`](/prompts/rules/react-doctor/no-static-element-interactions.md): Require static elements with click or key handlers to declare an interactive role, or be replaced with a semantic element.
- [`react-doctor/no-tight-body-leading`](/prompts/rules/react-doctor/no-tight-body-leading.md): Body copy has cramped line spacing
- [`react-doctor/no-tiny-text`](/prompts/rules/react-doctor/no-tiny-text.md): Use at least 12px for body content, 16px is ideal. Small text is hard to read, especially on high-DPI mobile screens
- [`react-doctor/no-transitioned-composite-widget-state`](/prompts/rules/react-doctor/no-transitioned-composite-widget-state.md): Composite widget state feedback is delayed
- [`react-doctor/no-transitioned-focus-ring`](/prompts/rules/react-doctor/no-transitioned-focus-ring.md): Keep focus indication immediate by removing only the transition that delays a ring or outline
- [`react-doctor/no-undersized-icon-button`](/prompts/rules/react-doctor/no-undersized-icon-button.md): Icon button target is smaller than 24px
- [`react-doctor/no-ungated-tailwind-animation`](/prompts/rules/react-doctor/no-ungated-tailwind-animation.md): Gate spatial Tailwind animation for reduced motion while preserving the same content, status, and actions
- [`react-doctor/no-uninformative-aria-label`](/prompts/rules/react-doctor/no-uninformative-aria-label.md): Uninformative aria-label
- [`react-doctor/prefer-html-dialog`](/prompts/rules/react-doctor/prefer-html-dialog.md): Replace the hand-rolled modal wrapper with a native <dialog> opened via dialog.showModal()
- [`react-doctor/prefer-tag-over-role`](/prompts/rules/react-doctor/prefer-tag-over-role.md): Replace role with the semantic HTML element when one exists.
- [`react-doctor/radio-input-missing-name`](/prompts/rules/react-doctor/radio-input-missing-name.md): Radio input missing name
- [`react-doctor/require-reduced-motion`](/prompts/rules/react-doctor/require-reduced-motion.md): Project may ship non-essential motion without an observable reduced-motion policy; confirm library defaults and real animation use before changing code
- [`react-doctor/role-button-requires-complete-keyboard-activation`](/prompts/rules/react-doctor/role-button-requires-complete-keyboard-activation.md): ARIA button handles only one activation key
- [`react-doctor/role-has-required-aria-props`](/prompts/rules/react-doctor/role-has-required-aria-props.md): Require elements with an ARIA role to carry that role's required aria-* state props, like aria-checked on role='checkbox'.
- [`react-doctor/role-supports-aria-props`](/prompts/rules/react-doctor/role-supports-aria-props.md): Use only aria-* props that are supported by the element's explicit or implicit ARIA role.
- [`react-doctor/scope`](/prompts/rules/react-doctor/scope.md): Disallow the scope attribute on elements other than th: it is only defined for table header cells.
- [`react-doctor/tabindex-no-positive`](/prompts/rules/react-doctor/tabindex-no-positive.md): Disallow positive tabIndex values, which override the natural tab order and make focus jump unpredictably.

## Architecture

- [`react-doctor/design-no-em-dash-in-jsx-text`](/prompts/rules/react-doctor/design-no-em-dash-in-jsx-text.md) **(creative-direction review)**: 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-axes`](/prompts/rules/react-doctor/design-no-redundant-padding-axes.md): 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-axes`](/prompts/rules/react-doctor/design-no-redundant-size-axes.md): Collapse `w-N h-N` to `size-N` (Tailwind v3.4+) when both axes match
- [`react-doctor/design-no-space-on-flex-children`](/prompts/rules/react-doctor/design-no-space-on-flex-children.md): 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-ellipsis`](/prompts/rules/react-doctor/design-no-three-period-ellipsis.md) **(creative-direction review)**: Use the typographic ellipsis "…" (or `&hellip;`) instead of three periods: pairs with action-with-followup labels ("Rename…", "Loading…")
- [`react-doctor/display-name`](/prompts/rules/react-doctor/display-name.md): Give each component a stable displayName so React DevTools shows a real name instead of "Unknown".
- [`react-doctor/forbid-component-props`](/prompts/rules/react-doctor/forbid-component-props.md): Configure forbidden props per component via the `forbidComponentProps.forbid` setting.
- [`react-doctor/forbid-dom-props`](/prompts/rules/react-doctor/forbid-dom-props.md): Configure forbidden DOM props via the `forbidDomProps.forbid` setting to keep disallowed attributes off DOM nodes.
- [`react-doctor/forbid-elements`](/prompts/rules/react-doctor/forbid-elements.md): Replace each configured forbidden element with its sanctioned component or wrapper.
- [`react-doctor/forward-ref-uses-ref`](/prompts/rules/react-doctor/forward-ref-uses-ref.md): Either accept a `ref` parameter in the forwardRef render function, or drop the forwardRef wrapper entirely.
- [`react-doctor/hook-use-state`](/prompts/rules/react-doctor/hook-use-state.md): Destructure useState as `const [thing, setThing] = useState(…)`.
- [`react-doctor/jsx-boolean-value`](/prompts/rules/react-doctor/jsx-boolean-value.md): Pick one boolean-attribute style codebase-wide (default: omit `={true}`, e.g. write `<C foo />`).
- [`react-doctor/jsx-curly-brace-presence`](/prompts/rules/react-doctor/jsx-curly-brace-presence.md): Pick a consistent quoting style for JSX literal values and drop redundant curly braces around plain strings.
- [`react-doctor/jsx-filename-extension`](/prompts/rules/react-doctor/jsx-filename-extension.md): Use .jsx / .tsx (or your project's chosen extension) for files containing JSX.
- [`react-doctor/jsx-fragments`](/prompts/rules/react-doctor/jsx-fragments.md): Pick one fragment style across the codebase: use the <></> shorthand by default.
- [`react-doctor/jsx-handler-names`](/prompts/rules/react-doctor/jsx-handler-names.md): Use the `on…` prefix for event-handler props and `handle…` for the functions that handle them.
- [`react-doctor/jsx-max-depth`](/prompts/rules/react-doctor/jsx-max-depth.md): Extract deeply nested JSX into smaller components to keep render trees readable.
- [`react-doctor/jsx-no-useless-fragment`](/prompts/rules/react-doctor/jsx-no-useless-fragment.md): Drop the fragment when it wraps a single child or holds multiple children directly under an HTML tag.
- [`react-doctor/jsx-pascal-case`](/prompts/rules/react-doctor/jsx-pascal-case.md): Rename custom JSX components to PascalCase.
- [`react-doctor/jsx-props-no-spreading`](/prompts/rules/react-doctor/jsx-props-no-spreading.md): List each prop explicitly so consumers can see what's being passed instead of spreading.
- [`react-doctor/no-clone-element`](/prompts/rules/react-doctor/no-clone-element.md): Pass children, render props, or Children.map instead of cloning elements with React.cloneElement.
- [`react-doctor/no-dark-mode-glow`](/prompts/rules/react-doctor/no-dark-mode-glow.md) **(creative-direction review)**: 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-props`](/prompts/rules/react-doctor/no-default-props.md): 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-names`](/prompts/rules/react-doctor/no-generic-handler-names.md): Rename to describe the action: e.g. `handleSubmit` → `saveUserProfile`, `handleClick` → `toggleSidebar`
- [`react-doctor/no-giant-component`](/prompts/rules/react-doctor/no-giant-component.md): Extract logical sections into focused components: `<UserHeader />`, `<UserActions />`, etc.
- [`react-doctor/no-gradient-text`](/prompts/rules/react-doctor/no-gradient-text.md) **(creative-direction review)**: 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-style`](/prompts/rules/react-doctor/no-inline-exhaustive-style.md): 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-props`](/prompts/rules/react-doctor/no-many-boolean-props.md): Split into compound components or named variants: `<Button.Primary />`, `<DialogConfirm />` instead of stacking `isPrimary`, `isConfirm` flags
- [`react-doctor/no-multi-comp`](/prompts/rules/react-doctor/no-multi-comp.md): Move secondary components into their own files.
- [`react-doctor/no-polymorphic-children`](/prompts/rules/react-doctor/no-polymorphic-children.md): Expose explicit subcomponents (`<Button.Text>`, `<Button.Icon>`) so consumers don't need to switch on `typeof children`
- [`react-doctor/no-prop-types`](/prompts/rules/react-doctor/no-prop-types.md): Move propTypes to TypeScript types: `type Props = { value: number }; function Component(props: Props)`: React 19 ignores runtime propTypes
- [`react-doctor/no-pure-black-background`](/prompts/rules/react-doctor/no-pure-black-background.md) **(creative-direction review)**: 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-children`](/prompts/rules/react-doctor/no-react-children.md): Pass children as props or render them directly instead of calling React.Children methods.
- [`react-doctor/no-react-dom-deprecated-apis`](/prompts/rules/react-doctor/no-react-dom-deprecated-apis.md): 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-apis`](/prompts/rules/react-doctor/no-react19-deprecated-apis.md): 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-update`](/prompts/rules/react-doctor/no-redundant-should-component-update.md): 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-render`](/prompts/rules/react-doctor/no-render-in-render.md): 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-children`](/prompts/rules/react-doctor/no-render-prop-children.md): 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-state`](/prompts/rules/react-doctor/no-set-state.md): Lift state up or use an external store instead of this.setState.
- [`react-doctor/no-side-tab-border`](/prompts/rules/react-doctor/no-side-tab-border.md) **(creative-direction review)**: Use a subtler accent (box-shadow inset, background gradient, or border-bottom) instead of a thick one-sided border
- [`react-doctor/no-unescaped-entities`](/prompts/rules/react-doctor/no-unescaped-entities.md): Replace bare ' / " / > / } characters in JSX text with HTML entities.
- [`react-doctor/no-wide-letter-spacing`](/prompts/rules/react-doctor/no-wide-letter-spacing.md): Reserve wide tracking (letter-spacing > 0.05em) for short uppercase labels, navigation items, and buttons: not body text
- [`react-doctor/no-z-index-9999`](/prompts/rules/react-doctor/no-z-index-9999.md): 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-components`](/prompts/rules/react-doctor/only-export-components.md): Move non-component exports out of files that export components.
- [`react-doctor/prefer-es6-class`](/prompts/rules/react-doctor/prefer-es6-class.md): Use one component style consistently: ES2015 `class extends React.Component` (default) over the legacy `createReactClass` factory.
- [`react-doctor/prefer-explicit-variants`](/prompts/rules/react-doctor/prefer-explicit-variants.md): Split into explicit variant components: render `<ThreadComposer />` and `<EditMessageComposer />` instead of one component switching subtrees on boolean props.
- [`react-doctor/prefer-function-component`](/prompts/rules/react-doctor/prefer-function-component.md): Re-write the class component as a function component using hooks.
- [`react-doctor/prefer-module-scope-pure-function`](/prompts/rules/react-doctor/prefer-module-scope-pure-function.md): 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-value`](/prompts/rules/react-doctor/prefer-module-scope-static-value.md): 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-memoization`](/prompts/rules/react-doctor/react-compiler-no-manual-memoization.md): 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-comp`](/prompts/rules/react-doctor/self-closing-comp.md): Use the self-closing form `<X />` for elements with no children.
- [`react-doctor/state-in-constructor`](/prompts/rules/react-doctor/state-in-constructor.md): Pick one state-initialization style for class components: class field or constructor: and use it consistently.
- [`react-doctor/zod-v4-no-deprecated-error-apis`](/prompts/rules/react-doctor/zod-v4-no-deprecated-error-apis.md): 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-customization`](/prompts/rules/react-doctor/zod-v4-no-deprecated-error-customization.md): Replace deprecated Zod error customization with the v4 unified { error } API: z.string({ error: "Required" })
- [`react-doctor/zod-v4-no-deprecated-schema-apis`](/prompts/rules/react-doctor/zod-v4-no-deprecated-schema-apis.md): 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-formats`](/prompts/rules/react-doctor/zod-v4-prefer-top-level-string-formats.md): Replace z.string().<format>() with the Zod 4 top-level format API, e.g. z.email() or z.uuid()

## Bugs

- [`react-doctor/class-component-missing-component-will-unmount-teardown`](/prompts/rules/react-doctor/class-component-missing-component-will-unmount-teardown.md): Class component acquires a resource with no teardown
- [`react-doctor/debounce-no-cleanup`](/prompts/rules/react-doctor/debounce-no-cleanup.md): Memoized debounce never cancelled on unmount
- [`react-doctor/effect-listener-cleanup-mismatch`](/prompts/rules/react-doctor/effect-listener-cleanup-mismatch.md): Effect cleanup does not match its event listener
- [`react-doctor/effect-listener-cleanup-reference-mismatch`](/prompts/rules/react-doctor/effect-listener-cleanup-reference-mismatch.md): Effect cleanup removes the wrong listener reference
- [`react-doctor/effect-observer-needs-disconnect`](/prompts/rules/react-doctor/effect-observer-needs-disconnect.md): Observer created in an effect never disconnected
- [`react-doctor/effect-raf-loop-needs-cancel`](/prompts/rules/react-doctor/effect-raf-loop-needs-cancel.md): requestAnimationFrame loop never cancelled
- [`react-doctor/effect-remove-listener-inline-handler`](/prompts/rules/react-doctor/effect-remove-listener-inline-handler.md): removeEventListener called with a fresh inline handler
- [`react-doctor/form-control-requires-name`](/prompts/rules/react-doctor/form-control-requires-name.md): Form control is omitted from named submission data
- [`react-doctor/hook-import-rename-loses-use-prefix`](/prompts/rules/react-doctor/hook-import-rename-loses-use-prefix.md): Hook import alias disables hook lint checks
- [`react-doctor/html-label-has-single-control`](/prompts/rules/react-doctor/html-label-has-single-control.md): Label wraps multiple native controls
- [`react-doctor/html-no-nested-form`](/prompts/rules/react-doctor/html-no-nested-form.md): Form nested inside another form
- [`react-doctor/ink-ctrl-c-handler-requires-exit-option`](/prompts/rules/react-doctor/ink-ctrl-c-handler-requires-exit-option.md): Ctrl-C handler is unreachable
- [`react-doctor/ink-newline-inside-text`](/prompts/rules/react-doctor/ink-newline-inside-text.md) **(retired)**: Retired: Ink Newline placement
- [`react-doctor/ink-no-bare-process-exit`](/prompts/rules/react-doctor/ink-no-bare-process-exit.md): Process exits before Ink cleanup
- [`react-doctor/ink-no-direct-raw-mode`](/prompts/rules/react-doctor/ink-no-direct-raw-mode.md): Raw mode changed during render
- [`react-doctor/ink-no-dom-host-elements`](/prompts/rules/react-doctor/ink-no-dom-host-elements.md): DOM element used in an Ink tree
- [`react-doctor/ink-no-dom-router`](/prompts/rules/react-doctor/ink-no-dom-router.md): DOM router used in an Ink tree
- [`react-doctor/ink-no-focus-in-render`](/prompts/rules/react-doctor/ink-no-focus-in-render.md): Ink focus changed during render
- [`react-doctor/ink-no-layout-inside-text`](/prompts/rules/react-doctor/ink-no-layout-inside-text.md): Layout component nested in Ink Text
- [`react-doctor/ink-no-live-hooks-in-render-to-string`](/prompts/rules/react-doctor/ink-no-live-hooks-in-render-to-string.md): Inert input hook used during string rendering
- [`react-doctor/ink-no-measure-element-in-render`](/prompts/rules/react-doctor/ink-no-measure-element-in-render.md): Ink element measured during render
- [`react-doctor/ink-no-multiple-static`](/prompts/rules/react-doctor/ink-no-multiple-static.md): Multiple unconditional Static regions in one render root
- [`react-doctor/ink-no-raw-text`](/prompts/rules/react-doctor/ink-no-raw-text.md): Raw text outside Ink Text
- [`react-doctor/ink-no-repeated-render`](/prompts/rules/react-doctor/ink-no-repeated-render.md): Ink render reused before unmount
- [`react-doctor/ink-prefer-use-paste`](/prompts/rules/react-doctor/ink-prefer-use-paste.md): Paste interpreted through useInput
- [`react-doctor/ink-static-is-append-only`](/prompts/rules/react-doctor/ink-static-is-append-only.md): Static receives a non-append-only collection
- [`react-doctor/ink-static-requires-key`](/prompts/rules/react-doctor/ink-static-requires-key.md): Static item root missing a key
- [`react-doctor/ink-suspense-requires-concurrent`](/prompts/rules/react-doctor/ink-suspense-requires-concurrent.md) **(retired)**: Retired: Ink Suspense rendering mode
- [`react-doctor/ink-use-reactive-window-size`](/prompts/rules/react-doctor/ink-use-reactive-window-size.md): Terminal dimensions read non-reactively
- [`react-doctor/ink-use-string-width-for-cursor`](/prompts/rules/react-doctor/ink-use-string-width-for-cursor.md): String length used as terminal column width
- [`react-doctor/ink-use-suspend-terminal`](/prompts/rules/react-doctor/ink-use-suspend-terminal.md): Interactive child process bypasses Ink suspension
- [`react-doctor/jsx-numeric-and-leaked-render`](/prompts/rules/react-doctor/jsx-numeric-and-leaked-render.md): Numeric && renders a stray 0
- [`react-doctor/mobx-no-make-auto-observable-in-inheritance`](/prompts/rules/react-doctor/mobx-no-make-auto-observable-in-inheritance.md): Unsupported MobX auto-observable inheritance
- [`react-doctor/mobx-no-observer-wrapped-memo`](/prompts/rules/react-doctor/mobx-no-observer-wrapped-memo.md): Invalid MobX observer wrapper order
- [`react-doctor/mobx-reaction-disposer-discarded`](/prompts/rules/react-doctor/mobx-reaction-disposer-discarded.md): MobX reaction disposer discarded
- [`react-doctor/motion-animate-presence-must-outlive-child`](/prompts/rules/react-doctor/motion-animate-presence-must-outlive-child.md): AnimatePresence unmounts with its exiting child
- [`react-doctor/motion-animate-presence-wait-single-child`](/prompts/rules/react-doctor/motion-animate-presence-wait-single-child.md): AnimatePresence wait mode has multiple children
- [`react-doctor/motion-create-in-render`](/prompts/rules/react-doctor/motion-create-in-render.md): Motion component is created during render
- [`react-doctor/motion-drag-axis-constraint-mismatch`](/prompts/rules/react-doctor/motion-drag-axis-constraint-mismatch.md): Motion drag constraints do not match the drag axis
- [`react-doctor/motion-imperative-animation-in-render`](/prompts/rules/react-doctor/motion-imperative-animation-in-render.md): Imperative Motion animation runs during render
- [`react-doctor/motion-keyframe-times-mismatch`](/prompts/rules/react-doctor/motion-keyframe-times-mismatch.md): Motion keyframes and times have different lengths
- [`react-doctor/motion-layout-on-inline-element`](/prompts/rules/react-doctor/motion-layout-on-inline-element.md): Motion layout animation targets an inline element
- [`react-doctor/motion-unstable-layout-id-in-iteration`](/prompts/rules/react-doctor/motion-unstable-layout-id-in-iteration.md): Motion layout ID is unstable in an iteration
- [`react-doctor/motion-use-transform-range-length`](/prompts/rules/react-doctor/motion-use-transform-range-length.md): Motion transform ranges have different lengths
- [`react-doctor/motion-value-subscription-in-render`](/prompts/rules/react-doctor/motion-value-subscription-in-render.md): Motion value subscription is created during render
- [`react-doctor/nextjs-async-dynamic-api-not-awaited`](/prompts/rules/react-doctor/nextjs-async-dynamic-api-not-awaited.md): Synchronous Next.js request API access
- [`react-doctor/nextjs-metadata-url-consistency`](/prompts/rules/react-doctor/nextjs-metadata-url-consistency.md): Canonical and Open Graph URLs disagree
- [`react-doctor/no-adjust-state-on-prop-change`](/prompts/rules/react-doctor/no-adjust-state-on-prop-change.md): Disallow adjusting state in an effect when a prop changes.
- [`react-doctor/no-arithmetic-on-optional-chained-operand`](/prompts/rules/react-doctor/no-arithmetic-on-optional-chained-operand.md): Multiplicative math on optional-chained value can be NaN
- [`react-doctor/no-array-find-result-member-access-without-guard`](/prompts/rules/react-doctor/no-array-find-result-member-access-without-guard.md): Unguarded member access on find() result
- [`react-doctor/no-array-index-deref-without-bounds-or-empty-guard`](/prompts/rules/react-doctor/no-array-index-deref-without-bounds-or-empty-guard.md): Array index result dereferenced without a guard
- [`react-doctor/no-async-effect-callback`](/prompts/rules/react-doctor/no-async-effect-callback.md): Async effect callback
- [`react-doctor/no-async-event-handler-without-reentry-guard`](/prompts/rules/react-doctor/no-async-event-handler-without-reentry-guard.md): Async mutating handler without re-entry guard
- [`react-doctor/no-boolean-toggle-without-functional-update`](/prompts/rules/react-doctor/no-boolean-toggle-without-functional-update.md): Boolean toggle reads a stale value
- [`react-doctor/no-broken-image-source`](/prompts/rules/react-doctor/no-broken-image-source.md): Image has no usable source
- [`react-doctor/no-call-component-as-function`](/prompts/rules/react-doctor/no-call-component-as-function.md): Component called as a function
- [`react-doctor/no-chain-state-updates`](/prompts/rules/react-doctor/no-chain-state-updates.md): Disallow chaining state changes in an effect.
- [`react-doctor/no-clipped-overlay`](/prompts/rules/react-doctor/no-clipped-overlay.md): Overlay can be clipped by an overflow container
- [`react-doctor/no-collapse-request-error-to-empty-state`](/prompts/rules/react-doctor/no-collapse-request-error-to-empty-state.md): Request failure rendered as an empty result
- [`react-doctor/no-collapsed-literal-or-chain-as-value`](/prompts/rules/react-doctor/no-collapsed-literal-or-chain-as-value.md): All-literal logical chain used as a multi-value expression
- [`react-doctor/no-conflicting-spring-options`](/prompts/rules/react-doctor/no-conflicting-spring-options.md): Motion spring mixes incompatible option modes
- [`react-doctor/no-controlled-input-value-without-state-update`](/prompts/rules/react-doctor/no-controlled-input-value-without-state-update.md): Controlled input value is a fixed literal
- [`react-doctor/no-create-object-url-in-render`](/prompts/rules/react-doctor/no-create-object-url-in-render.md): Object URL is created during render
- [`react-doctor/no-create-ref-in-function-component`](/prompts/rules/react-doctor/no-create-ref-in-function-component.md): createRef in function component
- [`react-doctor/no-deprecated-keyboard-event-keycode-which`](/prompts/rules/react-doctor/no-deprecated-keyboard-event-keycode-which.md): Deprecated KeyboardEvent keyCode or which
- [`react-doctor/no-effect-wrapper-discards-callback-cleanup-return`](/prompts/rules/react-doctor/no-effect-wrapper-discards-callback-cleanup-return.md): Effect wrapper discards forwarded cleanup return
- [`react-doctor/no-enter-submit-without-ime-composition-guard`](/prompts/rules/react-doctor/no-enter-submit-without-ime-composition-guard.md): Enter submit without IME composition guard
- [`react-doctor/no-fetch-response-used-without-status-check`](/prompts/rules/react-doctor/no-fetch-response-used-without-status-check.md): fetch Response consumed without status check
- [`react-doctor/no-fill-map-element-as-key`](/prompts/rules/react-doctor/no-fill-map-element-as-key.md): fill().map() first param is the element, not the index
- [`react-doctor/no-fixed-inside-transformed-ancestor`](/prompts/rules/react-doctor/no-fixed-inside-transformed-ancestor.md): Fixed element is scoped by a transformed ancestor
- [`react-doctor/no-floating-then-in-jsx-handler`](/prompts/rules/react-doctor/no-floating-then-in-jsx-handler.md): Floating .then in a JSX event handler
- [`react-doctor/no-hydration-branch-on-browser-global`](/prompts/rules/react-doctor/no-hydration-branch-on-browser-global.md): Render the same initial branch on the server and client instead of branching on a browser global during hydration.
- [`react-doctor/no-impure-call-at-module-scope`](/prompts/rules/react-doctor/no-impure-call-at-module-scope.md): Nondeterministic built-in at module scope
- [`react-doctor/no-impure-state-updater`](/prompts/rules/react-doctor/no-impure-state-updater.md): State updater has side effects
- [`react-doctor/no-indeterminate-attribute`](/prompts/rules/react-doctor/no-indeterminate-attribute.md): Indeterminate checkbox state set as an attribute
- [`react-doctor/no-inert-sticky-position`](/prompts/rules/react-doctor/no-inert-sticky-position.md): Sticky positioning has no inset
- [`react-doctor/no-loading-flag-reset-outside-finally`](/prompts/rules/react-doctor/no-loading-flag-reset-outside-finally.md): Loading flag reset outside finally
- [`react-doctor/no-locale-format-in-render`](/prompts/rules/react-doctor/no-locale-format-in-render.md): Locale/timezone formatting during render
- [`react-doctor/no-match-media-in-state-initializer`](/prompts/rules/react-doctor/no-match-media-in-state-initializer.md): matchMedia in state initializer
- [`react-doctor/no-mixed-srcset-descriptors`](/prompts/rules/react-doctor/no-mixed-srcset-descriptors.md): srcSet mixes width and density descriptors
- [`react-doctor/no-mutate-queried-dom-node-in-component`](/prompts/rules/react-doctor/no-mutate-queried-dom-node-in-component.md): Mutating a queried DOM node this component renders
- [`react-doctor/no-mutate-then-set-or-return-same-reference`](/prompts/rules/react-doctor/no-mutate-then-set-or-return-same-reference.md): State mutated in place then set by same reference
- [`react-doctor/no-mutating-array-method-on-prop-or-hook-result`](/prompts/rules/react-doctor/no-mutating-array-method-on-prop-or-hook-result.md): In-place array mutation on a prop or hook result
- [`react-doctor/no-non-literal-selector-query-without-try-catch`](/prompts/rules/react-doctor/no-non-literal-selector-query-without-try-catch.md): Unguarded querySelector with href-derived selector
- [`react-doctor/no-non-null-assertion-on-maybe-undefined-result`](/prompts/rules/react-doctor/no-non-null-assertion-on-maybe-undefined-result.md): Non-null assertion on a maybe-undefined result
- [`react-doctor/no-nondeterministic-id-value-in-render-body`](/prompts/rules/react-doctor/no-nondeterministic-id-value-in-render-body.md): Nondeterministic id generated in render body
- [`react-doctor/no-nullish-coalescing-arithmetic-precedence`](/prompts/rules/react-doctor/no-nullish-coalescing-arithmetic-precedence.md): Nullish coalescing swallowed by adjacent arithmetic
- [`react-doctor/no-object-keys-values-entries-on-maybe-undefined`](/prompts/rules/react-doctor/no-object-keys-values-entries-on-maybe-undefined.md): Object.keys/values/entries on maybe-undefined value
- [`react-doctor/no-object-or-array-coerced-to-string-in-template-literal`](/prompts/rules/react-doctor/no-object-or-array-coerced-to-string-in-template-literal.md): Object or lossy array coerced in a template literal
- [`react-doctor/no-pass-data-to-parent`](/prompts/rules/react-doctor/no-pass-data-to-parent.md): Disallow passing data to parents in an effect.
- [`react-doctor/no-pass-live-state-to-parent`](/prompts/rules/react-doctor/no-pass-live-state-to-parent.md): Disallow passing live state to parents in an effect.
- [`react-doctor/no-predicate-function-reference-in-boolean-position`](/prompts/rules/react-doctor/no-predicate-function-reference-in-boolean-position.md): Predicate function used without calling it
- [`react-doctor/no-promise-then-side-effect-in-effect-without-catch`](/prompts/rules/react-doctor/no-promise-then-side-effect-in-effect-without-catch.md): Effect promise .then sets state with no catch
- [`react-doctor/no-prop-callback-in-render`](/prompts/rules/react-doctor/no-prop-callback-in-render.md): Prop callback invoked during render
- [`react-doctor/no-ref-callback-cleanup-before-react-19`](/prompts/rules/react-doctor/no-ref-callback-cleanup-before-react-19.md): Ref cleanup requires React 19
- [`react-doctor/no-ref-current-in-render`](/prompts/rules/react-doctor/no-ref-current-in-render.md): Ref mutated during render
- [`react-doctor/no-set-state-after-await-in-effect`](/prompts/rules/react-doctor/no-set-state-after-await-in-effect.md): State update after await in an effect
- [`react-doctor/no-side-effect-in-state-updater-function`](/prompts/rules/react-doctor/no-side-effect-in-state-updater-function.md): Side effect inside a state updater function
- [`react-doctor/no-spread-props-over-defaults-clobbers-with-undefined`](/prompts/rules/react-doctor/no-spread-props-over-defaults-clobbers-with-undefined.md): Spread props over defaults can clobber with undefined
- [`react-doctor/no-stale-timer-ref`](/prompts/rules/react-doctor/no-stale-timer-ref.md): Cleared timer ref keeps the stale id
- [`react-doctor/no-static-motion-config-never`](/prompts/rules/react-doctor/no-static-motion-config-never.md): MotionConfig always ignores reduced motion
- [`react-doctor/no-string-false-on-boolean-attribute`](/prompts/rules/react-doctor/no-string-false-on-boolean-attribute.md): String true/false on a boolean attribute
- [`react-doctor/no-unescaped-dynamic-string-in-regexp`](/prompts/rules/react-doctor/no-unescaped-dynamic-string-in-regexp.md): Unescaped dynamic string in RegExp constructor
- [`react-doctor/no-unguarded-browser-global-at-module-scope`](/prompts/rules/react-doctor/no-unguarded-browser-global-at-module-scope.md): Browser global read at module scope
- [`react-doctor/no-unguarded-browser-global-in-render-or-hook-init`](/prompts/rules/react-doctor/no-unguarded-browser-global-in-render-or-hook-init.md): Browser global read during server render
- [`react-doctor/no-unguarded-numeric-input-parse`](/prompts/rules/react-doctor/no-unguarded-numeric-input-parse.md): Unguarded numeric parse of an input value
- [`react-doctor/no-unguarded-throwing-parse-call`](/prompts/rules/react-doctor/no-unguarded-throwing-parse-call.md): Unguarded call to a throwing parse API
- [`react-doctor/no-unsafe-json-parse`](/prompts/rules/react-doctor/no-unsafe-json-parse.md): Unsafe JSON.parse dereference
- [`react-doctor/no-whole-object-default-losing-per-key-defaults`](/prompts/rules/react-doctor/no-whole-object-default-losing-per-key-defaults.md): Whole-object param default loses per-key defaults
- [`react-doctor/no-whole-object-dep-with-member-reads`](/prompts/rules/react-doctor/no-whole-object-dep-with-member-reads.md): Whole props object in deps while only members are read
- [`react-doctor/pointer-capture-needs-cancel-handler`](/prompts/rules/react-doctor/pointer-capture-needs-cancel-handler.md): Captured pointer interaction has no cancellation path
- [`react-doctor/query-floating-mutate-async`](/prompts/rules/react-doctor/query-floating-mutate-async.md): Floating mutateAsync rejection
- [`react-doctor/query-no-mutation-in-effect-as-read`](/prompts/rules/react-doctor/query-no-mutation-in-effect-as-read.md): Mutation driven from an effect as a read
- [`react-doctor/r3f-no-advancing-clock-in-use-frame`](/prompts/rules/react-doctor/r3f-no-advancing-clock-in-use-frame.md): Clock advanced inside useFrame
- [`react-doctor/r3f-no-async-use-frame`](/prompts/rules/react-doctor/r3f-no-async-use-frame.md): Async useFrame callback
- [`react-doctor/r3f-no-dispose-loader-cache`](/prompts/rules/react-doctor/r3f-no-dispose-loader-cache.md): Disposal of a cached R3F loader asset
- [`react-doctor/r3f-no-duplicate-primitive-object`](/prompts/rules/react-doctor/r3f-no-duplicate-primitive-object.md): Primitive object mounted twice
- [`react-doctor/r3f-no-extend-in-render`](/prompts/rules/react-doctor/r3f-no-extend-in-render.md): R3F catalogue extension during render
- [`react-doctor/r3f-no-fresh-portal-container`](/prompts/rules/react-doctor/r3f-no-fresh-portal-container.md): Fresh R3F portal container
- [`react-doctor/r3f-no-imperative-attach-of-managed-ref`](/prompts/rules/react-doctor/r3f-no-imperative-attach-of-managed-ref.md): Imperative attachment of an R3F-managed ref
- [`react-doctor/r3f-no-mutate-loader-cache`](/prompts/rules/react-doctor/r3f-no-mutate-loader-cache.md): Mutation of a cached R3F loader asset
- [`react-doctor/r3f-no-mutating-pointer-event-data`](/prompts/rules/react-doctor/r3f-no-mutating-pointer-event-data.md): Mutation of shared R3F pointer-event data
- [`react-doctor/r3f-no-null-loader-input`](/prompts/rules/react-doctor/r3f-no-null-loader-input.md): Nullish R3F loader input
- [`react-doctor/r3f-no-object-pointer-capture`](/prompts/rules/react-doctor/r3f-no-object-pointer-capture.md): Pointer capture called on an R3F scene object
- [`react-doctor/r3f-no-use-frame-dependency-array`](/prompts/rules/react-doctor/r3f-no-use-frame-dependency-array.md): Dependency array passed to useFrame
- [`react-doctor/r3f-require-frame-delta`](/prompts/rules/react-doctor/r3f-require-frame-delta.md): Frame-rate-dependent animation
- [`react-doctor/r3f-require-global-effect-cleanup`](/prompts/rules/react-doctor/r3f-require-global-effect-cleanup.md): Unreleased global R3F render-loop effect
- [`react-doctor/r3f-require-instanced-buffer-update`](/prompts/rules/react-doctor/r3f-require-instanced-buffer-update.md): Instanced mesh buffer is not marked for upload
- [`react-doctor/r3f-require-projection-matrix-update`](/prompts/rules/react-doctor/r3f-require-projection-matrix-update.md): Missing camera projection-matrix update
- [`react-doctor/r3f-require-render-with-positive-priority`](/prompts/rules/react-doctor/r3f-require-render-with-positive-priority.md): Positive useFrame priority without a render
- [`react-doctor/r3f-require-root-unmount`](/prompts/rules/react-doctor/r3f-require-root-unmount.md): R3F root without unmount cleanup
- [`react-doctor/r3f-webgpu-canvas-prop-compatibility`](/prompts/rules/react-doctor/r3f-webgpu-canvas-prop-compatibility.md): Incompatible R3F Canvas renderer prop
- [`react-doctor/r3f-webgpu-no-gl-state`](/prompts/rules/react-doctor/r3f-webgpu-no-gl-state.md): WebGPU root reads deprecated gl state
- [`react-doctor/r3f-webgpu-no-js-uniform-branch`](/prompts/rules/react-doctor/r3f-webgpu-no-js-uniform-branch.md): JavaScript branch reads a TSL uniform value
- [`react-doctor/r3f-webgpu-no-legacy-effect-composer`](/prompts/rules/react-doctor/r3f-webgpu-no-legacy-effect-composer.md): Legacy EffectComposer inside an R3F WebGPU Canvas
- [`react-doctor/r3f-webgpu-no-legacy-material-api`](/prompts/rules/react-doctor/r3f-webgpu-no-legacy-material-api.md): Legacy shader material API inside an R3F WebGPU Canvas
- [`react-doctor/r3f-webgpu-no-unregistered-pipeline-pass`](/prompts/rules/react-doctor/r3f-webgpu-no-unregistered-pipeline-pass.md): Unregistered WebGPU pipeline pass
- [`react-doctor/react-router-descendant-routes-require-splat`](/prompts/rules/react-doctor/react-router-descendant-routes-require-splat.md): Parent path cannot match descendant routes
- [`react-doctor/react-router-guard-aborted-handle-error`](/prompts/rules/react-doctor/react-router-guard-aborted-handle-error.md): Aborted requests are reported as errors
- [`react-doctor/react-router-internal-route-anchor`](/prompts/rules/react-doctor/react-router-internal-route-anchor.md): Internal route uses a document navigation
- [`react-doctor/react-router-nested-route-requires-outlet`](/prompts/rules/react-doctor/react-router-nested-route-requires-outlet.md): Nested routes have no Outlet
- [`react-doctor/react-router-no-catch-middleware-next`](/prompts/rules/react-doctor/react-router-no-catch-middleware-next.md): Middleware catch cannot observe downstream errors
- [`react-doctor/react-router-no-client-module-in-server-render`](/prompts/rules/react-doctor/react-router-no-client-module-in-server-render.md): Client-only module rendered on the server
- [`react-doctor/react-router-no-duplicate-route-id`](/prompts/rules/react-doctor/react-router-no-duplicate-route-id.md): Duplicate route ID
- [`react-doctor/react-router-no-empty-leaf-route`](/prompts/rules/react-doctor/react-router-no-empty-leaf-route.md): Leaf route renders nothing
- [`react-doctor/react-router-no-invalid-absolute-child-path`](/prompts/rules/react-doctor/react-router-no-invalid-absolute-child-path.md): Absolute child path escapes its parent
- [`react-doctor/react-router-no-invalid-lazy-route-properties`](/prompts/rules/react-doctor/react-router-no-invalid-lazy-route-properties.md): Immutable route property returned from lazy
- [`react-doctor/react-router-no-invalid-splat-path`](/prompts/rules/react-doctor/react-router-no-invalid-splat-path.md): Invalid splat route path
- [`react-doctor/react-router-no-loader-request-body`](/prompts/rules/react-doctor/react-router-no-loader-request-body.md): Loader reads a request body
- [`react-doctor/react-router-no-middleware-response-body-consumption`](/prompts/rules/react-doctor/react-router-no-middleware-response-body-consumption.md): Middleware consumes the outgoing response body
- [`react-doctor/react-router-no-multiple-blockers`](/prompts/rules/react-doctor/react-router-no-multiple-blockers.md): Multiple blockers in one component
- [`react-doctor/react-router-no-multiple-middleware-next`](/prompts/rules/react-doctor/react-router-no-multiple-middleware-next.md): Middleware continuation called twice
- [`react-doctor/react-router-no-multiple-set-search-params-in-tick`](/prompts/rules/react-doctor/react-router-no-multiple-set-search-params-in-tick.md): Search params updated multiple times
- [`react-doctor/react-router-no-navigate-in-render`](/prompts/rules/react-doctor/react-router-no-navigate-in-render.md): navigate() called during render
- [`react-doctor/react-router-no-nested-router`](/prompts/rules/react-doctor/react-router-no-nested-router.md): Router nested inside another router
- [`react-doctor/react-router-no-redirect-in-try-catch`](/prompts/rules/react-doctor/react-router-no-redirect-in-try-catch.md): redirect() inside try-catch
- [`react-doctor/react-router-no-route-module-environment-suffix`](/prompts/rules/react-doctor/react-router-no-route-module-environment-suffix.md): Route module has an environment suffix
- [`react-doctor/react-router-no-router-in-render`](/prompts/rules/react-doctor/react-router-no-router-in-render.md): Router created during render
- [`react-doctor/react-router-no-session-mutation-in-loader`](/prompts/rules/react-doctor/react-router-no-session-mutation-in-loader.md): Loader mutates session state
- [`react-doctor/react-router-no-static-cookie-expires`](/prompts/rules/react-doctor/react-router-no-static-cookie-expires.md): Cookie expiry is fixed at module load
- [`react-doctor/react-router-no-unsynchronized-search-params-mutation`](/prompts/rules/react-doctor/react-router-no-unsynchronized-search-params-mutation.md): Search params mutated without navigation
- [`react-doctor/react-router-no-use-loader-data-in-error-ui`](/prompts/rules/react-doctor/react-router-no-use-loader-data-in-error-ui.md): Error UI assumes loader data exists
- [`react-doctor/react-router-require-root-error-boundary`](/prompts/rules/react-doctor/react-router-require-root-error-boundary.md): Root route lacks an error boundary
- [`react-doctor/react-router-resource-link-requires-reload`](/prompts/rules/react-doctor/react-router-resource-link-requires-reload.md): Resource link intercepted as navigation
- [`react-doctor/react-router-return-navigation-promise-in-transition`](/prompts/rules/react-doctor/react-router-return-navigation-promise-in-transition.md): Transition drops a navigation promise
- [`react-doctor/react-router-server-middleware-return-response`](/prompts/rules/react-doctor/react-router-server-middleware-return-response.md): Server middleware drops the Response
- [`react-doctor/react-router-session-mutation-requires-commit`](/prompts/rules/react-doctor/react-router-session-mutation-requires-commit.md): Session mutation is not committed
- [`react-doctor/react-router-valid-route-object`](/prompts/rules/react-doctor/react-router-valid-route-object.md): Contradictory route object
- [`react-doctor/remotion-calculate-metadata-fetch-signal`](/prompts/rules/react-doctor/remotion-calculate-metadata-fetch-signal.md): calculateMetadata fetch ignores abortSignal
- [`react-doctor/remotion-deterministic-randomness`](/prompts/rules/react-doctor/remotion-deterministic-randomness.md): Randomness changes between rendered frames
- [`react-doctor/remotion-no-css-animation`](/prompts/rules/react-doctor/remotion-no-css-animation.md): CSS animation is not frame-driven
- [`react-doctor/remotion-no-css-transition`](/prompts/rules/react-doctor/remotion-no-css-transition.md): CSS transition is not frame-driven
- [`react-doctor/remotion-no-css-url-assets`](/prompts/rules/react-doctor/remotion-no-css-url-assets.md): CSS URL asset can flicker in Remotion
- [`react-doctor/remotion-no-module-scope-delay-render`](/prompts/rules/react-doctor/remotion-no-module-scope-delay-render.md): Module-scoped delayRender blocks every composition
- [`react-doctor/remotion-no-native-media-elements`](/prompts/rules/react-doctor/remotion-no-native-media-elements.md): Native media element bypasses Remotion loading
- [`react-doctor/remotion-no-next-image`](/prompts/rules/react-doctor/remotion-no-next-image.md): Next.js Image can flicker in Remotion
- [`react-doctor/remotion-stable-delay-render-handle`](/prompts/rules/react-doctor/remotion-stable-delay-render-handle.md): delayRender handle is recreated during render
- [`react-doctor/rn-bottom-sheet-no-ignored-scroll-prop`](/prompts/rules/react-doctor/rn-bottom-sheet-no-ignored-scroll-prop.md): Ignored BottomSheetScrollView prop
- [`react-doctor/rn-bottom-sheet-no-state-in-on-animate`](/prompts/rules/react-doctor/rn-bottom-sheet-no-state-in-on-animate.md): React state update in Bottom Sheet onAnimate
- [`react-doctor/rn-bottom-sheet-use-integrated-scrollable`](/prompts/rules/react-doctor/rn-bottom-sheet-use-integrated-scrollable.md): React Native scrollable inside a Bottom Sheet
- [`react-doctor/rn-platform-shaking-use-direct-import`](/prompts/rules/react-doctor/rn-platform-shaking-use-direct-import.md): Platform reached through React Native namespace
- [`react-doctor/rn-reanimated-4-no-legacy-spring-thresholds`](/prompts/rules/react-doctor/rn-reanimated-4-no-legacy-spring-thresholds.md): Legacy Reanimated spring threshold
- [`react-doctor/rn-reanimated-4-no-removed-api`](/prompts/rules/react-doctor/rn-reanimated-4-no-removed-api.md): API removed in Reanimated 4
- [`react-doctor/rn-reanimated-4-use-worklets-scheduler`](/prompts/rules/react-doctor/rn-reanimated-4-use-worklets-scheduler.md): Scheduler moved to Worklets
- [`react-doctor/shadcn-tabs-trigger-requires-list`](/prompts/rules/react-doctor/shadcn-tabs-trigger-requires-list.md): Tabs trigger is outside TabsList
- [`react-doctor/styled-components-non-transient-custom-prop-on-intrinsic-element`](/prompts/rules/react-doctor/styled-components-non-transient-custom-prop-on-intrinsic-element.md): Non-transient custom prop on styled intrinsic element
- [`react-doctor/tanstack-start-missing-scripts`](/prompts/rules/react-doctor/tanstack-start-missing-scripts.md): Root route missing Scripts
- [`react-doctor/three-no-async-animation-loop`](/prompts/rules/react-doctor/three-no-async-animation-loop.md): Async Three.js animation callback
- [`react-doctor/three-on-before-compile-require-program-cache-key`](/prompts/rules/react-doctor/three-on-before-compile-require-program-cache-key.md): onBeforeCompile variant lacks a program cache key
- [`react-doctor/three-raw-shader-require-fragment-float-precision`](/prompts/rules/react-doctor/three-raw-shader-require-fragment-float-precision.md): Raw fragment shader lacks float precision
- [`react-doctor/three-raw-shader-require-glsl3-version`](/prompts/rules/react-doctor/three-raw-shader-require-glsl3-version.md): Raw shader uses GLSL 3 syntax without GLSL3
- [`react-doctor/three-require-animation-mixer-cleanup`](/prompts/rules/react-doctor/three-require-animation-mixer-cleanup.md): Unreleased Three.js animation mixer actions
- [`react-doctor/three-require-controls-cleanup`](/prompts/rules/react-doctor/three-require-controls-cleanup.md): Undisposed imperative Three.js controls
- [`react-doctor/three-require-frame-delta`](/prompts/rules/react-doctor/three-require-frame-delta.md): Frame-rate-dependent Three.js animation
- [`react-doctor/three-require-instanced-buffer-update`](/prompts/rules/react-doctor/three-require-instanced-buffer-update.md): Three.js instanced mesh buffer is not marked for upload
- [`react-doctor/three-require-postprocessing-cleanup`](/prompts/rules/react-doctor/three-require-postprocessing-cleanup.md): Undisposed Three.js postprocessing resource
- [`react-doctor/three-require-projection-matrix-update`](/prompts/rules/react-doctor/three-require-projection-matrix-update.md): Missing Three.js camera projection-matrix update
- [`react-doctor/three-require-render-target-cleanup`](/prompts/rules/react-doctor/three-require-render-target-cleanup.md): Undisposed Three.js render target
- [`react-doctor/three-require-renderer-cleanup`](/prompts/rules/react-doctor/three-require-renderer-cleanup.md): Undisposed Three.js renderer
- [`react-doctor/three-shader-no-constant-out-of-bounds-index`](/prompts/rules/react-doctor/three-shader-no-constant-out-of-bounds-index.md): Shader uses a constant out-of-bounds index
- [`react-doctor/three-shader-no-derivatives-in-nonuniform-flow`](/prompts/rules/react-doctor/three-shader-no-derivatives-in-nonuniform-flow.md): Shader derivative runs in non-uniform control flow
- [`react-doctor/three-shader-no-invalid-clamp-bounds`](/prompts/rules/react-doctor/three-shader-no-invalid-clamp-bounds.md): Shader uses invalid clamp bounds
- [`react-doctor/three-shader-no-invalid-constant-bit-operations`](/prompts/rules/react-doctor/three-shader-no-invalid-constant-bit-operations.md): Shader uses invalid constant bit operations
- [`react-doctor/three-shader-no-invalid-constant-math`](/prompts/rules/react-doctor/three-shader-no-invalid-constant-math.md): Shader uses invalid constant math
- [`react-doctor/three-shader-no-invalid-smoothstep-edges`](/prompts/rules/react-doctor/three-shader-no-invalid-smoothstep-edges.md): Shader uses invalid smoothstep edges
- [`react-doctor/three-shader-no-redeclared-builtins`](/prompts/rules/react-doctor/three-shader-no-redeclared-builtins.md): ShaderMaterial redeclares a Three.js builtin
- [`react-doctor/three-shader-no-version-directive`](/prompts/rules/react-doctor/three-shader-no-version-directive.md): Three shader source contains a version directive
- [`react-doctor/three-shader-require-matching-uniforms`](/prompts/rules/react-doctor/three-shader-require-matching-uniforms.md): Shader stages declare incompatible uniforms
- [`react-doctor/three-shader-require-matching-varyings`](/prompts/rules/react-doctor/three-shader-require-matching-varyings.md): Shader stage interface does not match
- [`react-doctor/three-shader-require-position-on-all-paths`](/prompts/rules/react-doctor/three-shader-require-position-on-all-paths.md): Vertex shader leaves gl_Position undefined
- [`react-doctor/three-shader-require-uniform-bindings`](/prompts/rules/react-doctor/three-shader-require-uniform-bindings.md): Shader uniform has no material binding
- [`react-doctor/three-tsl-no-js-uniform-branch`](/prompts/rules/react-doctor/three-tsl-no-js-uniform-branch.md): JavaScript branch reads a TSL uniform value
- [`react-doctor/three-webgpu-no-legacy-effect-composer`](/prompts/rules/react-doctor/three-webgpu-no-legacy-effect-composer.md): Legacy EffectComposer used with WebGPURenderer
- [`react-doctor/three-webgpu-no-legacy-material-api`](/prompts/rules/react-doctor/three-webgpu-no-legacy-material-api.md): Legacy material API used with WebGPURenderer
- [`react-doctor/valtio-no-proxy-read-in-render`](/prompts/rules/react-doctor/valtio-no-proxy-read-in-render.md): Valtio proxy read during render
- [`react-doctor/valtio-no-snapshot-in-callback`](/prompts/rules/react-doctor/valtio-no-snapshot-in-callback.md): Valtio snapshot read in a callback
- [`react-doctor/waapi-animation-in-render`](/prompts/rules/react-doctor/waapi-animation-in-render.md): Web Animation starts during render
- [`react-doctor/web-animation-offsets-valid`](/prompts/rules/react-doctor/web-animation-offsets-valid.md): Invalid Web Animation keyframe offsets
- [`react-doctor/zustand-no-get-during-initialization`](/prompts/rules/react-doctor/zustand-no-get-during-initialization.md): Zustand get() called during store initialization
- [`react-doctor/zustand-no-mutating-state`](/prompts/rules/react-doctor/zustand-no-mutating-state.md): Zustand state mutated in place
- [`react-doctor/zustand-no-whole-store-destructure`](/prompts/rules/react-doctor/zustand-no-whole-store-destructure.md): Whole Zustand store subscribed during render

## Bundle Size

- [`react-doctor/no-barrel-import`](/prompts/rules/react-doctor/no-barrel-import.md): Import from the direct path: `import { Button } from './components/Button'` instead of `./components`
- [`react-doctor/no-dynamic-import-path`](/prompts/rules/react-doctor/no-dynamic-import-path.md): Use a string-literal path: `import('./feature/heavy.js')` so the bundler can split this chunk
- [`react-doctor/no-full-lodash-import`](/prompts/rules/react-doctor/no-full-lodash-import.md): Import the specific function: `import debounce from 'lodash/debounce'`: saves ~70kb
- [`react-doctor/no-moment`](/prompts/rules/react-doctor/no-moment.md): Replace with `import { format } from 'date-fns'` (tree-shakeable) or `import dayjs from 'dayjs'` (2kb)
- [`react-doctor/no-undeferred-third-party`](/prompts/rules/react-doctor/no-undeferred-third-party.md): Use `next/script` with `strategy="lazyOnload"` or add the `defer` attribute
- [`react-doctor/prefer-dynamic-import`](/prompts/rules/react-doctor/prefer-dynamic-import.md): Lazy-load non-critical heavy UI with a framework-appropriate boundary while preserving exports and SSR behavior
- [`react-doctor/use-lazy-motion`](/prompts/rules/react-doctor/use-lazy-motion.md): Consider LazyMotion for routes that ship the full Motion component; verify the installed package, required features, and measured bundle impact

## Correctness

- [`react-doctor/button-has-type`](/prompts/rules/react-doctor/button-has-type.md): Set type="button" (or "submit"/"reset") explicitly on every <button> so it never defaults to submit.
- [`react-doctor/checked-requires-onchange-or-readonly`](/prompts/rules/react-doctor/checked-requires-onchange-or-readonly.md): Pair `checked` with `onChange={…}` (controlled) or `readOnly` (display-only), and never combine `checked` with `defaultChecked`.
- [`react-doctor/client-localstorage-no-version`](/prompts/rules/react-doctor/client-localstorage-no-version.md): Bake a version into the storage key (e.g. "myKey:v1"); a future schema change can ignore old data instead of crashing on it
- [`react-doctor/exhaustive-deps`](/prompts/rules/react-doctor/exhaustive-deps.md): Match the deps array to what the hook callback actually captures, or stabilize/move recreated values instead of blindly adding them.
- [`react-doctor/html-no-invalid-paragraph-child`](/prompts/rules/react-doctor/html-no-invalid-paragraph-child.md): Replace the wrapping <p> with a <div>, or hoist the block-level child out of the paragraph
- [`react-doctor/html-no-invalid-table-nesting`](/prompts/rules/react-doctor/html-no-invalid-table-nesting.md): Author each table element under its required host parent: thead/tbody/tfoot in <table>, tr in a row group, td/th in <tr>
- [`react-doctor/html-no-nested-interactive`](/prompts/rules/react-doctor/html-no-nested-interactive.md): Hoist the inner interactive element out, or make the outer one a non-interactive wrapper: change <a><a/></a> to <a/> next to <a/>, or wrap with <div>/<span>
- [`react-doctor/jsx-key`](/prompts/rules/react-doctor/jsx-key.md): Require a stable key on elements rendered from arrays or iterators so React can reconcile list items correctly.
- [`react-doctor/jsx-no-comment-textnodes`](/prompts/rules/react-doctor/jsx-no-comment-textnodes.md): Wrap JSX comments in `{/* … */}` so they're parsed as comments, not rendered as literal child text.
- [`react-doctor/jsx-no-duplicate-props`](/prompts/rules/react-doctor/jsx-no-duplicate-props.md): Disallow duplicate props on a JSX element: React silently keeps only the last value.
- [`react-doctor/jsx-no-script-url`](/prompts/rules/react-doctor/jsx-no-script-url.md): Disallow javascript: URLs in href: they execute arbitrary code, and React 19 throws on them.
- [`react-doctor/jsx-no-undef`](/prompts/rules/react-doctor/jsx-no-undef.md): Import the component or fix the typo so the JSX element name resolves to a real binding.
- [`react-doctor/jsx-props-no-spread-multi`](/prompts/rules/react-doctor/jsx-props-no-spread-multi.md): Spread each unique expression at most once per JSX element.
- [`react-doctor/motion-animate-presence-requires-key`](/prompts/rules/react-doctor/motion-animate-presence-requires-key.md): Give each direct AnimatePresence child a stable identity when multiple direct elements can coexist
- [`react-doctor/no-array-index-as-key`](/prompts/rules/react-doctor/no-array-index-as-key.md): Use a stable unique identifier: `key={item.id}` or `key={item.slug}`: index keys break on reorder/filter
- [`react-doctor/no-children-prop`](/prompts/rules/react-doctor/no-children-prop.md): Disallow passing children as an explicit prop: nest content between the opening and closing tags instead.
- [`react-doctor/no-create-context-in-render`](/prompts/rules/react-doctor/no-create-context-in-render.md): Move createContext to module scope so its Context identity stays stable across renders
- [`react-doctor/no-create-store-in-render`](/prompts/rules/react-doctor/no-create-store-in-render.md): Hoist the store/atom/observable construction to module scope: const store = create(...) outside the component.
- [`react-doctor/no-danger`](/prompts/rules/react-doctor/no-danger.md): Flag dangerouslySetInnerHTML: raw HTML injection bypasses JSX escaping and invites cross-site scripting.
- [`react-doctor/no-danger-with-children`](/prompts/rules/react-doctor/no-danger-with-children.md): Use either children or dangerouslySetInnerHTML on an element, never both.
- [`react-doctor/no-direct-mutation-state`](/prompts/rules/react-doctor/no-direct-mutation-state.md): Disallow mutating this.state directly in class components: use setState so React schedules a re-render.
- [`react-doctor/no-document-start-view-transition`](/prompts/rules/react-doctor/no-document-start-view-transition.md): Use React <ViewTransition> only in a React canary build that provides it; otherwise keep a guarded document.startViewTransition progressive enhancement
- [`react-doctor/no-find-dom-node`](/prompts/rules/react-doctor/no-find-dom-node.md): Use refs (useRef/createRef) to access DOM nodes instead of the removed findDOMNode API.
- [`react-doctor/no-is-mounted`](/prompts/rules/react-doctor/no-is-mounted.md): Disallow the removed legacy isMounted() API: cancel async work instead of guarding setState.
- [`react-doctor/no-jsx-element-type`](/prompts/rules/react-doctor/no-jsx-element-type.md): Widen the return type from JSX.Element to React.ReactNode: function App(): React.ReactNode
- [`react-doctor/no-legacy-class-lifecycles`](/prompts/rules/react-doctor/no-legacy-class-lifecycles.md): Migrate legacy pre-commit class lifecycles to concurrent-safe patterns; their UNSAFE_ aliases remain available but deprecated in React 19
- [`react-doctor/no-legacy-context-api`](/prompts/rules/react-doctor/no-legacy-context-api.md): Replace `childContextTypes` + `getChildContext` with `const MyContext = createContext(...)` + `<MyContext.Provider value={...}>`; replace `contextTypes` with `static contextType = MyContext` (single context) or `useContext()` / `use()` from a function component. The provider and every consumer must migrate together: partial migrations leave consumers reading the wrong context.
- [`react-doctor/no-mixed-animation-owners`](/prompts/rules/react-doctor/no-mixed-animation-owners.md): Avoid CSS and Motion simultaneously owning the same animated property on one element
- [`react-doctor/no-namespace`](/prompts/rules/react-doctor/no-namespace.md): Drop the namespace and use a plain (Pascal-cased) component or DOM tag.
- [`react-doctor/no-nested-component-definition`](/prompts/rules/react-doctor/no-nested-component-definition.md): Move to a separate file or to module scope above the parent component
- [`react-doctor/no-prevent-default`](/prompts/rules/react-doctor/no-prevent-default.md): Use `<form action={serverAction}>` (works without JS) or `<button>` instead of `<a>` with preventDefault
- [`react-doctor/no-random-key`](/prompts/rules/react-doctor/no-random-key.md): Replace the fresh-each-render key with a stable id from the item: key={item.id}
- [`react-doctor/no-render-return-value`](/prompts/rules/react-doctor/no-render-return-value.md): Disallow using the return value of ReactDOM.render: a legacy escape hatch removed in React 19.
- [`react-doctor/no-string-refs`](/prompts/rules/react-doctor/no-string-refs.md): Disallow legacy string refs like ref='node': use createRef, useRef, or callback refs.
- [`react-doctor/no-this-in-sfc`](/prompts/rules/react-doctor/no-this-in-sfc.md): Use the function's `props` parameter instead of `this.props` in stateless function components.
- [`react-doctor/no-uncontrolled-input`](/prompts/rules/react-doctor/no-uncontrolled-input.md): Pass an explicit initial value to `useState` (e.g. `useState("")` instead of `useState()`), add `onChange` (or `readOnly` to opt out) when you supply `value`, and drop `defaultValue` on controlled inputs: React ignores it
- [`react-doctor/no-unknown-property`](/prompts/rules/react-doctor/no-unknown-property.md): Disallow unknown or mis-cased DOM attributes in JSX, like class instead of className.
- [`react-doctor/no-unsafe`](/prompts/rules/react-doctor/no-unsafe.md): Replace UNSAFE_componentWillMount / WillReceiveProps / WillUpdate with their modern lifecycle equivalents.
- [`react-doctor/react-in-jsx-scope`](/prompts/rules/react-doctor/react-in-jsx-scope.md): If on React 17+ with the new JSX transform, disable this rule; otherwise import React at the top of the file.
- [`react-doctor/rendering-animate-svg-wrapper`](/prompts/rules/react-doctor/rendering-animate-svg-wrapper.md): Review Motion-style props on a plain SVG; a wrapper is not a universal SVG performance fix
- [`react-doctor/rendering-conditional-render`](/prompts/rules/react-doctor/rendering-conditional-render.md): Change to `{items.length > 0 && <List />}` or use a ternary: `{items.length ? <List /> : null}`
- [`react-doctor/rendering-hydration-mismatch-time`](/prompts/rules/react-doctor/rendering-hydration-mismatch-time.md): Provide stable server/client time or identity data; reserve suppressHydrationWarning for an unavoidable one-level text or attribute mismatch
- [`react-doctor/require-render-return`](/prompts/rules/react-doctor/require-render-return.md): Require class component render() methods to return a value: a missing return renders nothing.
- [`react-doctor/rules-of-hooks`](/prompts/rules/react-doctor/rules-of-hooks.md): Enforce the Rules of Hooks: call hooks only at the top level of components and custom hooks, never in conditions or loops.
- [`react-doctor/style-prop-object`](/prompts/rules/react-doctor/style-prop-object.md): Pass the `style` prop as an object literal like `{{ color: 'red' }}`, never a string or other primitive.
- [`react-doctor/void-dom-elements-no-children`](/prompts/rules/react-doctor/void-dom-elements-no-children.md): Remove children from the void element, or use a non-void element if children are needed.

## Design

- [`react-doctor/no-ease-in-motion`](/prompts/rules/react-doctor/no-ease-in-motion.md) **(creative-direction review)**: 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-stagger`](/prompts/rules/react-doctor/no-excessive-motion-stagger.md) **(creative-direction review)**: Review stagger spacing above 80ms in the context of item count, total tail, frequency, and product intent
- [`react-doctor/no-inline-bounce-easing`](/prompts/rules/react-doctor/no-inline-bounce-easing.md) **(creative-direction review)**: Review overshoot and bounce against the product motion system instead of treating one curve as universal
- [`react-doctor/no-long-transition-duration`](/prompts/rules/react-doctor/no-long-transition-duration.md) **(creative-direction review)**: 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-zero`](/prompts/rules/react-doctor/no-scale-from-zero.md) **(creative-direction review)**: Review scale-to-zero entrances and exits; ordinary product UI usually reads more smoothly from a small nonzero scale plus opacity

## Maintainability

- [`react-doctor/circular-dependency`](/prompts/rules/react-doctor/circular-dependency.md): A runtime import cycle can observe partially initialized exports
- [`react-doctor/duplicate-jsx-subtree`](/prompts/rules/react-doctor/duplicate-jsx-subtree.md): Substantial JSX structure repeats across distinct React composition roots and may belong in a shared component
- [`react-doctor/no-auto-scrolling-content`](/prompts/rules/react-doctor/no-auto-scrolling-content.md): Content auto-scrolls forever
- [`react-doctor/no-common-root-font`](/prompts/rules/react-doctor/no-common-root-font.md) **(creative-direction review)**: Page root uses a generic default font choice
- [`react-doctor/no-decorative-blur-orb`](/prompts/rules/react-doctor/no-decorative-blur-orb.md) **(creative-direction review)**: Empty blurred color orb decorates the layout
- [`react-doctor/no-decorative-grid-background`](/prompts/rules/react-doctor/no-decorative-grid-background.md) **(creative-direction review)**: Surface draws a decorative grid background
- [`react-doctor/no-decorative-pulse`](/prompts/rules/react-doctor/no-decorative-pulse.md) **(creative-direction review)**: Stable copy pulses for attention
- [`react-doctor/no-decorative-radial-spotlight`](/prompts/rules/react-doctor/no-decorative-radial-spotlight.md) **(creative-direction review)**: Large decorative radial spotlight
- [`react-doctor/no-default-purple-page-gradient`](/prompts/rules/react-doctor/no-default-purple-page-gradient.md) **(creative-direction review)**: Page root uses a default purple-spectrum gradient
- [`react-doctor/no-default-warm-page-surface`](/prompts/rules/react-doctor/no-default-warm-page-surface.md) **(creative-direction review)**: Page defaults to a warm off-white surface
- [`react-doctor/no-deprecated-tailwind-class`](/prompts/rules/react-doctor/no-deprecated-tailwind-class.md): Deprecated Tailwind v4 utility
- [`react-doctor/no-dynamic-tailwind-class-fragment`](/prompts/rules/react-doctor/no-dynamic-tailwind-class-fragment.md): Tailwind utility is assembled dynamically
- [`react-doctor/no-emoji-heading-decoration`](/prompts/rules/react-doctor/no-emoji-heading-decoration.md) **(creative-direction review)**: Heading uses emoji as decoration
- [`react-doctor/no-empty-card-shell`](/prompts/rules/react-doctor/no-empty-card-shell.md) **(creative-direction review)**: Empty semantic element renders as a card
- [`react-doctor/no-excessive-card-surfaces`](/prompts/rules/react-doctor/no-excessive-card-surfaces.md) **(creative-direction review)**: Page boxes too many groups into cards
- [`react-doctor/no-excessive-centered-copy`](/prompts/rules/react-doctor/no-excessive-centered-copy.md) **(creative-direction review)**: Page centers multiple body-copy blocks
- [`react-doctor/no-excessive-font-families`](/prompts/rules/react-doctor/no-excessive-font-families.md) **(creative-direction review)**: Page mixes too many font families
- [`react-doctor/no-excessive-pill-treatment`](/prompts/rules/react-doctor/no-excessive-pill-treatment.md) **(creative-direction review)**: Page overuses pill-shaped treatments
- [`react-doctor/no-fake-browser-chrome`](/prompts/rules/react-doctor/no-fake-browser-chrome.md) **(creative-direction review)**: Preview redraws decorative browser chrome
- [`react-doctor/no-flat-page-type-scale`](/prompts/rules/react-doctor/no-flat-page-type-scale.md) **(creative-direction review)**: Page typography uses a compressed size range
- [`react-doctor/no-full-viewport-centered-hero`](/prompts/rules/react-doctor/no-full-viewport-centered-hero.md) **(creative-direction review)**: Hero uses a full-viewport centered template
- [`react-doctor/no-full-viewport-width`](/prompts/rules/react-doctor/no-full-viewport-width.md): Full viewport width causes overflow
- [`react-doctor/no-generic-marketing-copy`](/prompts/rules/react-doctor/no-generic-marketing-copy.md) **(creative-direction review)**: Page uses generic marketing language
- [`react-doctor/no-generic-purple-blue-icon-gradient`](/prompts/rules/react-doctor/no-generic-purple-blue-icon-gradient.md) **(creative-direction review)**: Compact icon tile uses a generic purple-to-blue gradient
- [`react-doctor/no-hairline-border-wide-shadow`](/prompts/rules/react-doctor/no-hairline-border-wide-shadow.md): Hairline border is paired with a diffuse shadow
- [`react-doctor/no-hero-eyebrow-chip`](/prompts/rules/react-doctor/no-hero-eyebrow-chip.md) **(creative-direction review)**: Hero uses a decorative eyebrow label
- [`react-doctor/no-high-complexity-react-function`](/prompts/rules/react-doctor/no-high-complexity-react-function.md): A React component or custom hook exceeds the cyclomatic or cognitive complexity threshold
- [`react-doctor/no-icon-tile-heading-stack`](/prompts/rules/react-doctor/no-icon-tile-heading-stack.md) **(creative-direction review)**: Card stacks an icon tile above its heading
- [`react-doctor/no-image-hover-transform`](/prompts/rules/react-doctor/no-image-hover-transform.md): Image scales or rotates on hover
- [`react-doctor/no-inline-hoc-on-component`](/prompts/rules/react-doctor/no-inline-hoc-on-component.md): Function component defined inline inside an HOC call
- [`react-doctor/no-italic-serif-display-heading`](/prompts/rules/react-doctor/no-italic-serif-display-heading.md) **(creative-direction review)**: Display heading combines italic serif styling
- [`react-doctor/no-layout-shifting-interaction-state`](/prompts/rules/react-doctor/no-layout-shifting-interaction-state.md): Interaction state changes layout geometry
- [`react-doctor/no-manufactured-contrast-copy`](/prompts/rules/react-doctor/no-manufactured-contrast-copy.md) **(creative-direction review)**: Page repeatedly uses manufactured contrast copy
- [`react-doctor/no-mixed-icon-libraries`](/prompts/rules/react-doctor/no-mixed-icon-libraries.md): File mixes visual icon families
- [`react-doctor/no-monotonous-page-spacing`](/prompts/rules/react-doctor/no-monotonous-page-spacing.md) **(creative-direction review)**: Page repeats one spacing value throughout
- [`react-doctor/no-nested-card-surface`](/prompts/rules/react-doctor/no-nested-card-surface.md) **(creative-direction review)**: Card surface is nested inside another card
- [`react-doctor/no-numbered-section-markers`](/prompts/rules/react-doctor/no-numbered-section-markers.md) **(creative-direction review)**: Styled numbers are used as section decoration
- [`react-doctor/no-overloaded-hover-state`](/prompts/rules/react-doctor/no-overloaded-hover-state.md) **(creative-direction review)**: Hover state stacks too many effects
- [`react-doctor/no-oversized-long-heading`](/prompts/rules/react-doctor/no-oversized-long-heading.md): Long headline uses an oversized display scale
- [`react-doctor/no-pill-navigation-count`](/prompts/rules/react-doctor/no-pill-navigation-count.md) **(creative-direction review)**: Navigation count uses a pill badge
- [`react-doctor/no-placeholder-persona-copy`](/prompts/rules/react-doctor/no-placeholder-persona-copy.md): Page renders a placeholder persona
- [`react-doctor/no-pulsing-status-dot`](/prompts/rules/react-doctor/no-pulsing-status-dot.md) **(creative-direction review)**: Decorative status dot pulses continuously
- [`react-doctor/no-pure-black-shadow`](/prompts/rules/react-doctor/no-pure-black-shadow.md) **(creative-direction review)**: Surface uses a pure-black shadow
- [`react-doctor/no-radial-halo`](/prompts/rules/react-doctor/no-radial-halo.md) **(creative-direction review)**: Saturated radial halo on a dark surface
- [`react-doctor/no-redundant-display-class`](/prompts/rules/react-doctor/no-redundant-display-class.md): Redundant display utility
- [`react-doctor/no-redundant-title-tooltip`](/prompts/rules/react-doctor/no-redundant-title-tooltip.md): Title tooltip repeats visible control text
- [`react-doctor/no-repeated-container-text`](/prompts/rules/react-doctor/no-repeated-container-text.md) **(creative-direction review)**: A card repeats the same text in distinct slots
- [`react-doctor/no-repeated-emoji-tiles`](/prompts/rules/react-doctor/no-repeated-emoji-tiles.md) **(creative-direction review)**: Page repeats boxed emoji as feature icons
- [`react-doctor/no-repeated-glass-surfaces`](/prompts/rules/react-doctor/no-repeated-glass-surfaces.md) **(creative-direction review)**: Page repeats glass panels
- [`react-doctor/no-repeated-hover-scale`](/prompts/rules/react-doctor/no-repeated-hover-scale.md) **(creative-direction review)**: Hover scaling repeats across the page
- [`react-doctor/no-repeated-kicker-labels`](/prompts/rules/react-doctor/no-repeated-kicker-labels.md) **(creative-direction review)**: Repeated tracked labels scaffold multiple sections
- [`react-doctor/no-repeated-placeholder-navigation`](/prompts/rules/react-doctor/no-repeated-placeholder-navigation.md): Navigation repeats placeholder destinations
- [`react-doctor/no-repeated-section-shells`](/prompts/rules/react-doctor/no-repeated-section-shells.md) **(creative-direction review)**: Page repeats the same padded section shell
- [`react-doctor/no-repeating-gradient-decoration`](/prompts/rules/react-doctor/no-repeating-gradient-decoration.md) **(creative-direction review)**: Surface uses a repeating gradient texture
- [`react-doctor/no-shape-assembled-illustration`](/prompts/rules/react-doctor/no-shape-assembled-illustration.md) **(creative-direction review)**: Large illustration is assembled from primitive shapes
- [`react-doctor/no-svg-currentcolor-with-fill-class`](/prompts/rules/react-doctor/no-svg-currentcolor-with-fill-class.md): currentColor fights a fill/stroke class
- [`react-doctor/no-symmetric-text-button-padding`](/prompts/rules/react-doctor/no-symmetric-text-button-padding.md) **(creative-direction review)**: Text button uses symmetric padding
- [`react-doctor/no-tight-all-caps-heading`](/prompts/rules/react-doctor/no-tight-all-caps-heading.md): Long all-caps heading has collision-prone leading
- [`react-doctor/no-tight-display-tracking`](/prompts/rules/react-doctor/no-tight-display-tracking.md): Display heading uses the tightest tracking preset
- [`react-doctor/no-tiny-uppercase-tracked-label`](/prompts/rules/react-doctor/no-tiny-uppercase-tracked-label.md): Tiny label combines uppercase text and decorative tracking
- [`react-doctor/no-uniform-feature-card-grid`](/prompts/rules/react-doctor/no-uniform-feature-card-grid.md) **(creative-direction review)**: Feature grid repeats one card recipe
- [`react-doctor/no-uppercase-mono-label`](/prompts/rules/react-doctor/no-uppercase-mono-label.md) **(creative-direction review)**: Short label uses uppercase monospace styling
- [`react-doctor/no-uppercase-tracked-navigation-label`](/prompts/rules/react-doctor/no-uppercase-tracked-navigation-label.md) **(creative-direction review)**: Navigation label uses uppercase tracking
- [`react-doctor/prefer-dvh-over-vh`](/prompts/rules/react-doctor/prefer-dvh-over-vh.md): Use dvh instead of vh for full height
- [`react-doctor/prefer-tabular-numeric-data`](/prompts/rules/react-doctor/prefer-tabular-numeric-data.md): Changing table numbers use proportional figures
- [`react-doctor/prefer-truncate-shorthand`](/prompts/rules/react-doctor/prefer-truncate-shorthand.md): Use truncate shorthand
- [`react-doctor/r3f-no-internal-imports`](/prompts/rules/react-doctor/r3f-no-internal-imports.md): Private R3F import
- [`react-doctor/react-router-v8-no-meta-data-field`](/prompts/rules/react-doctor/react-router-v8-no-meta-data-field.md): Removed meta data field
- [`react-doctor/react-router-v8-no-react-router-dom-import`](/prompts/rules/react-doctor/react-router-v8-no-react-router-dom-import.md): Removed react-router-dom import
- [`react-doctor/react-router-v8-no-removed-future-flags`](/prompts/rules/react-doctor/react-router-v8-no-removed-future-flags.md): Removed React Router future flag
- [`react-doctor/require-autoplay-video-poster`](/prompts/rules/react-doctor/require-autoplay-video-poster.md): Autoplay video has no poster frame
- [`react-doctor/require-scale-reveal-transform-origin`](/prompts/rules/react-doctor/require-scale-reveal-transform-origin.md): Scaled menu omits its transform origin
- [`react-doctor/styled-components-duplicate-css-property-in-block`](/prompts/rules/react-doctor/styled-components-duplicate-css-property-in-block.md): Duplicate CSS property in styled block
- [`react-doctor/unused-dependency`](/prompts/rules/react-doctor/unused-dependency.md): A production dependency has no detected project usage
- [`react-doctor/unused-dev-dependency`](/prompts/rules/react-doctor/unused-dev-dependency.md): A development dependency has no detected project usage
- [`react-doctor/unused-export`](/prompts/rules/react-doctor/unused-export.md): A reachable module exports a value that no module imports
- [`react-doctor/unused-file`](/prompts/rules/react-doctor/unused-file.md): A source file is unreachable from every discovered project entry point
- [`react-doctor/unused-type`](/prompts/rules/react-doctor/unused-type.md): A reachable module exports a type that no module imports

## Next.js

- [`react-doctor/nextjs-async-client-component`](/prompts/rules/react-doctor/nextjs-async-client-component.md): Fetch data in a parent Server Component and pass it as props, or use useQuery/useSWR in the client component
- [`react-doctor/nextjs-error-boundary-missing-use-client`](/prompts/rules/react-doctor/nextjs-error-boundary-missing-use-client.md): Add `'use client'` as the first statement of error.tsx / global-error.tsx so the error boundary becomes a Client Component.
- [`react-doctor/nextjs-global-error-missing-html-body`](/prompts/rules/react-doctor/nextjs-global-error-missing-html-body.md): Wrap the global-error UI in `<html><body>...</body></html>` because the root layout unmounts when global-error renders
- [`react-doctor/nextjs-image-missing-sizes`](/prompts/rules/react-doctor/nextjs-image-missing-sizes.md): Add sizes for responsive behavior: `sizes="(max-width: 768px) 100vw, 50vw"` matching your layout breakpoints
- [`react-doctor/nextjs-inline-script-missing-id`](/prompts/rules/react-doctor/nextjs-inline-script-missing-id.md): Add `id="descriptive-name"` so Next.js can track, deduplicate, and re-execute the script correctly
- [`react-doctor/nextjs-missing-metadata`](/prompts/rules/react-doctor/nextjs-missing-metadata.md): Add `export const metadata = { title: '...', description: '...' }` or `export async function generateMetadata()`
- [`react-doctor/nextjs-no-a-element`](/prompts/rules/react-doctor/nextjs-no-a-element.md): `import Link from 'next/link'`: enables client-side navigation, prefetching, and preserves scroll position
- [`react-doctor/nextjs-no-client-fetch-for-server-data`](/prompts/rules/react-doctor/nextjs-no-client-fetch-for-server-data.md): Remove 'use client' and fetch directly in the Server Component: no API round-trip, secrets stay on server
- [`react-doctor/nextjs-no-client-side-redirect`](/prompts/rules/react-doctor/nextjs-no-client-side-redirect.md): Avoid redirects inside useEffect. Use an event handler, middleware, or server-side redirect (App Router: redirect() from next/navigation; Pages Router: getServerSideProps redirect)
- [`react-doctor/nextjs-no-css-link`](/prompts/rules/react-doctor/nextjs-no-css-link.md): Import CSS directly: `import './styles.css'` or use CSS Modules: `import styles from './Button.module.css'`
- [`react-doctor/nextjs-no-default-export-in-route-handler`](/prompts/rules/react-doctor/nextjs-no-default-export-in-route-handler.md): Remove the default export from this route.ts and export the handler as a named HTTP method instead: export async function GET(request: Request) { ... }
- [`react-doctor/nextjs-no-edge-og-runtime`](/prompts/rules/react-doctor/nextjs-no-edge-og-runtime.md): Remove `export const runtime = "edge"` from OG image route files so they use the default Node.js runtime
- [`react-doctor/nextjs-no-font-link`](/prompts/rules/react-doctor/nextjs-no-font-link.md): `import { Inter } from "next/font/google"`: self-hosted, zero layout shift, no render-blocking requests
- [`react-doctor/nextjs-no-google-analytics-script`](/prompts/rules/react-doctor/nextjs-no-google-analytics-script.md): Replace manual GA script with the optimized component: import { GoogleAnalytics } from '@next/third-parties/google'
- [`react-doctor/nextjs-no-head-import`](/prompts/rules/react-doctor/nextjs-no-head-import.md): Use the Metadata API instead: `export const metadata = { title: '...' }` or `export async function generateMetadata()`
- [`react-doctor/nextjs-no-img-element`](/prompts/rules/react-doctor/nextjs-no-img-element.md): `import Image from 'next/image'`: provides automatic WebP/AVIF, lazy loading, and responsive srcset
- [`react-doctor/nextjs-no-native-script`](/prompts/rules/react-doctor/nextjs-no-native-script.md): `import Script from "next/script"`: use `strategy="afterInteractive"` for analytics or `"lazyOnload"` for widgets
- [`react-doctor/nextjs-no-polyfill-script`](/prompts/rules/react-doctor/nextjs-no-polyfill-script.md): Next.js includes polyfills for fetch, Promise, Object.assign, Array.from, and 50+ others automatically
- [`react-doctor/nextjs-no-redirect-in-try-catch`](/prompts/rules/react-doctor/nextjs-no-redirect-in-try-catch.md): Move the redirect/notFound call outside the try block, or add `unstable_rethrow(error)` in the catch
- [`react-doctor/nextjs-no-script-in-head`](/prompts/rules/react-doctor/nextjs-no-script-in-head.md): Move <Script> out of <Head>: next/script ignores head placement, so a Script nested in Head silently never loads
- [`react-doctor/nextjs-no-use-search-params-without-suspense`](/prompts/rules/react-doctor/nextjs-no-use-search-params-without-suspense.md): Wrap the component using useSearchParams: `<Suspense fallback={<Skeleton />}><SearchComponent /></Suspense>`
- [`react-doctor/nextjs-no-vercel-og-import`](/prompts/rules/react-doctor/nextjs-no-vercel-og-import.md): Replace the @vercel/og import with import { ImageResponse } from "next/og"

## Performance

- [`react-doctor/advanced-event-handler-refs`](/prompts/rules/react-doctor/advanced-event-handler-refs.md): Store the handler in a ref and have the listener read `handlerRef.current()`: the subscription stays put while the latest handler is always called
- [`react-doctor/async-await-in-loop`](/prompts/rules/react-doctor/async-await-in-loop.md): Review awaited loops for work that is safe to start concurrently within ordering, authorization, and service limits
- [`react-doctor/async-defer-await`](/prompts/rules/react-doctor/async-defer-await.md): Move an `await` after an early-return guard only when skipping the operation and its possible error is intended
- [`react-doctor/async-parallel`](/prompts/rules/react-doctor/async-parallel.md): Review sequential awaits for operations that are safe to start together within failure and concurrency constraints
- [`react-doctor/client-passive-event-listeners`](/prompts/rules/react-doctor/client-passive-event-listeners.md): Add `{ passive: true }` as the third argument: `addEventListener('scroll', handler, { passive: true })`. Only do this if the handler does NOT call `event.preventDefault()`: passive listeners silently ignore `preventDefault()`, which breaks features like pull-to-refresh suppression, custom gestures, and nested-scroll containment.
- [`react-doctor/context-provider-value-from-unmemoized-local-literal`](/prompts/rules/react-doctor/context-provider-value-from-unmemoized-local-literal.md): Context value from an unmemoized local literal
- [`react-doctor/ink-prefer-use-animation`](/prompts/rules/react-doctor/ink-prefer-use-animation.md): Animation loop implemented with setInterval
- [`react-doctor/js-async-reduce-without-awaited-acc`](/prompts/rules/react-doctor/js-async-reduce-without-awaited-acc.md): Await the accumulator inside an async .reduce reducer: const acc = await previous; ...; return acc;
- [`react-doctor/js-batch-dom-css`](/prompts/rules/react-doctor/js-batch-dom-css.md): Batch DOM/CSS reads and writes: interleaving them inside a loop causes layout thrashing. Read first, then write
- [`react-doctor/js-cache-property-access`](/prompts/rules/react-doctor/js-cache-property-access.md): Review repeated deep member reads only in a measured hot loop with a stable receiver
- [`react-doctor/js-cache-storage`](/prompts/rules/react-doctor/js-cache-storage.md): Reuse a browser-storage read only within one operation when snapshot semantics are intended
- [`react-doctor/js-combine-iterations`](/prompts/rules/react-doctor/js-combine-iterations.md): Fuse chained array iterations only when profiling identifies the chain as a hot path
- [`react-doctor/js-early-exit`](/prompts/rules/react-doctor/js-early-exit.md): Add an early `return` / `continue` to flatten deep nesting and short-circuit when the predicate is already known
- [`react-doctor/js-flatmap-filter`](/prompts/rules/react-doctor/js-flatmap-filter.md): Review map-plus-falsy-filter chains only when measurement justifies fusing them
- [`react-doctor/js-hoist-intl`](/prompts/rules/react-doctor/js-hoist-intl.md): Hoist `new Intl.NumberFormat(...)` to module scope or wrap in `useMemo`: Intl constructors allocate dozens of objects per locale lookup
- [`react-doctor/js-hoist-regexp`](/prompts/rules/react-doctor/js-hoist-regexp.md): Move only loop-invariant, non-stateful RegExp construction out of a measured hot loop
- [`react-doctor/js-index-maps`](/prompts/rules/react-doctor/js-index-maps.md): Build a lookup index only for repeated stable-key searches that amortize its construction
- [`react-doctor/js-length-check-first`](/prompts/rules/react-doctor/js-length-check-first.md): Short-circuit with `a.length === b.length && a.every((x, i) => x === b[i])`: unequal-length arrays exit immediately
- [`react-doctor/js-min-max-loop`](/prompts/rules/react-doctor/js-min-max-loop.md): Replace sorting for an extremum only when numeric or comparator semantics can be preserved
- [`react-doctor/js-set-map-lookups`](/prompts/rules/react-doctor/js-set-map-lookups.md): Use Set or Map only when repeated lookups amortize construction for a stable collection
- [`react-doctor/js-tosorted-immutable`](/prompts/rules/react-doctor/js-tosorted-immutable.md): Prefer `toSorted()` as a clearer non-mutating API when the target runtime supports it
- [`react-doctor/jsx-no-constructed-context-values`](/prompts/rules/react-doctor/jsx-no-constructed-context-values.md): Memoize the context value with useMemo/useCallback or hoist it outside the render
- [`react-doctor/jsx-no-jsx-as-prop`](/prompts/rules/react-doctor/jsx-no-jsx-as-prop.md): Hoist the inline JSX out of render or memoize it with useMemo so the prop value is stable across renders.
- [`react-doctor/jsx-no-new-array-as-prop`](/prompts/rules/react-doctor/jsx-no-new-array-as-prop.md): Memoize the array (useMemo) or hoist it outside the component instead of allocating a new one each render.
- [`react-doctor/jsx-no-new-function-as-prop`](/prompts/rules/react-doctor/jsx-no-new-function-as-prop.md): Memoize the callback (useCallback) or hoist it outside the component to keep a stable reference across renders.
- [`react-doctor/jsx-no-new-object-as-prop`](/prompts/rules/react-doctor/jsx-no-new-object-as-prop.md): Memoize the object (useMemo) or hoist it outside the component.
- [`react-doctor/motion-value-constructor-in-render`](/prompts/rules/react-doctor/motion-value-constructor-in-render.md): Motion value is recreated during render
- [`react-doctor/no-array-index-key`](/prompts/rules/react-doctor/no-array-index-key.md): Use a stable, data-derived key instead of the array iteration index.
- [`react-doctor/no-create-object-url-without-revoke`](/prompts/rules/react-doctor/no-create-object-url-without-revoke.md): createObjectURL without revokeObjectURL
- [`react-doctor/no-document-write`](/prompts/rules/react-doctor/no-document-write.md): document.write/writeln
- [`react-doctor/no-eager-new-in-use-state-initializer`](/prompts/rules/react-doctor/no-eager-new-in-use-state-initializer.md): Eager constructor in useState initializer
- [`react-doctor/no-flush-sync`](/prompts/rules/react-doctor/no-flush-sync.md): Remove flushSync when a synchronous DOM commit is unnecessary; startTransition is only for explicitly non-urgent work, not a semantic replacement
- [`react-doctor/no-global-css-variable-animation`](/prompts/rules/react-doctor/no-global-css-variable-animation.md): Review frame-by-frame writes to a root CSS custom property; inherited variables can make a large document recalculate styles
- [`react-doctor/no-img-lazy-with-high-fetchpriority`](/prompts/rules/react-doctor/no-img-lazy-with-high-fetchpriority.md): Lazy image with high fetchPriority
- [`react-doctor/no-img-without-dimensions`](/prompts/rules/react-doctor/no-img-without-dimensions.md): Image has no reserved layout space
- [`react-doctor/no-inline-prop-on-memo-component`](/prompts/rules/react-doctor/no-inline-prop-on-memo-component.md): Hoist the inline `() => ...` / `[]` / `{}` to a stable reference (useMemo, useCallback, or module scope) so the memoized child doesn't re-render every parent render
- [`react-doctor/no-json-parse-stringify-clone`](/prompts/rules/react-doctor/no-json-parse-stringify-clone.md): JSON parse/stringify deep clone
- [`react-doctor/no-large-animated-blur`](/prompts/rules/react-doctor/no-large-animated-blur.md): Review blur animation above 10px when the affected area, frequency, and target devices make paint or compositing cost material
- [`react-doctor/no-layout-property-animation`](/prompts/rules/react-doctor/no-layout-property-animation.md): Animating layout properties can recalculate geometry every frame; confirm the animation is large, repeated, or measurably costly before changing its semantics
- [`react-doctor/no-layout-transition-inline`](/prompts/rules/react-doctor/no-layout-transition-inline.md): Review inline transitions of layout properties and preserve the distinction between visual movement and real document reflow
- [`react-doctor/no-permanent-will-change`](/prompts/rules/react-doctor/no-permanent-will-change.md): Use will-change only after profiling shows a benefit; broad or long-lived hints can retain costly layers
- [`react-doctor/no-spread-accumulator-in-reduce`](/prompts/rules/react-doctor/no-spread-accumulator-in-reduce.md): Accumulator spread in reduce is quadratic
- [`react-doctor/no-srcset-without-sizes`](/prompts/rules/react-doctor/no-srcset-without-sizes.md): Responsive image omits sizes
- [`react-doctor/no-sync-xhr`](/prompts/rules/react-doctor/no-sync-xhr.md): Synchronous XMLHttpRequest
- [`react-doctor/no-tailwind-layout-transition`](/prompts/rules/react-doctor/no-tailwind-layout-transition.md): Review Tailwind transitions of layout properties and preserve real reflow when it is part of the interaction
- [`react-doctor/no-transition-all`](/prompts/rules/react-doctor/no-transition-all.md): List specific properties: `transition: "opacity 200ms, transform 200ms"`: or in Tailwind use `transition-colors`, `transition-opacity`, or `transition-transform`
- [`react-doctor/no-unbounded-animation-frame-loop`](/prompts/rules/react-doctor/no-unbounded-animation-frame-loop.md): Animation frame loop has no stop path
- [`react-doctor/no-unstable-nested-components`](/prompts/rules/react-doctor/no-unstable-nested-components.md): Hoist nested components to module scope or memoize them: never define one inside another.
- [`react-doctor/no-unthrottled-scroll-mutation`](/prompts/rules/react-doctor/no-unthrottled-scroll-mutation.md): Scroll handler drives animation every event
- [`react-doctor/no-usememo-simple-expression`](/prompts/rules/react-doctor/no-usememo-simple-expression.md): Remove useMemo: property access, math, and ternaries are already cheap without memoization
- [`react-doctor/prefer-motion-transform-property`](/prompts/rules/react-doctor/prefer-motion-transform-property.md): Prefer a complete Motion transform string only when profiling shows acceleration is crucial and transform ownership can remain correct
- [`react-doctor/prefer-stable-empty-fallback`](/prompts/rules/react-doctor/prefer-stable-empty-fallback.md): Hoist a module-level const EMPTY = [] (or {}) and use it as the || / ?? fallback so the memoised child sees a stable reference
- [`react-doctor/r3f-cap-device-pixel-ratio`](/prompts/rules/react-doctor/r3f-cap-device-pixel-ratio.md): Unbounded device pixel ratio
- [`react-doctor/r3f-limit-shadowed-point-lights`](/prompts/rules/react-doctor/r3f-limit-shadowed-point-lights.md): Too many shadow-casting point lights
- [`react-doctor/r3f-no-allocation-in-pointer-move`](/prompts/rules/react-doctor/r3f-no-allocation-in-pointer-move.md): Allocation inside an R3F pointer-move handler
- [`react-doctor/r3f-no-clone-in-use-frame`](/prompts/rules/react-doctor/r3f-no-clone-in-use-frame.md): Three.js clone inside useFrame
- [`react-doctor/r3f-no-deep-use-three-selector`](/prompts/rules/react-doctor/r3f-no-deep-use-three-selector.md): useThree selector reads a mutable Three.js field
- [`react-doctor/r3f-no-extend-three-namespace`](/prompts/rules/react-doctor/r3f-no-extend-three-namespace.md): Whole Three.js namespace registered with R3F
- [`react-doctor/r3f-no-fresh-use-three-selector`](/prompts/rules/react-doctor/r3f-no-fresh-use-three-selector.md): Fresh useThree selector result
- [`react-doctor/r3f-no-inline-primitive-object`](/prompts/rules/react-doctor/r3f-no-inline-primitive-object.md): Inline primitive object
- [`react-doctor/r3f-no-inline-resource-prop`](/prompts/rules/react-doctor/r3f-no-inline-resource-prop.md): Inline R3F resource prop
- [`react-doctor/r3f-no-manual-canvas-resize`](/prompts/rules/react-doctor/r3f-no-manual-canvas-resize.md): Manual resize loop for an R3F-owned renderer
- [`react-doctor/r3f-no-new-in-use-frame`](/prompts/rules/react-doctor/r3f-no-new-in-use-frame.md): Allocation inside useFrame
- [`react-doctor/r3f-no-recursive-raf-with-use-frame`](/prompts/rules/react-doctor/r3f-no-recursive-raf-with-use-frame.md): Competing animation loop alongside useFrame
- [`react-doctor/r3f-no-state-in-pointer-move`](/prompts/rules/react-doctor/r3f-no-state-in-pointer-move.md): React state update inside an R3F pointer-move handler
- [`react-doctor/r3f-no-state-in-use-frame`](/prompts/rules/react-doctor/r3f-no-state-in-use-frame.md): React state update inside useFrame
- [`react-doctor/r3f-no-sync-readback-in-use-frame`](/prompts/rules/react-doctor/r3f-no-sync-readback-in-use-frame.md): Synchronous readback inside useFrame
- [`react-doctor/r3f-no-unstable-args`](/prompts/rules/react-doctor/r3f-no-unstable-args.md): Unstable R3F constructor args
- [`react-doctor/r3f-prefer-use-loader`](/prompts/rules/react-doctor/r3f-prefer-use-loader.md): Imperative Three.js loader in a React effect
- [`react-doctor/r3f-require-owned-texture-cleanup`](/prompts/rules/react-doctor/r3f-require-owned-texture-cleanup.md): Locally owned Three.js texture is not disposed
- [`react-doctor/react-router-loader-fetch-forwards-signal`](/prompts/rules/react-doctor/react-router-loader-fetch-forwards-signal.md): Loader fetch ignores cancellation
- [`react-doctor/react-router-loader-parallel-fetch`](/prompts/rules/react-doctor/react-router-loader-parallel-fetch.md): Independent loader work runs sequentially
- [`react-doctor/react-router-prefer-route-lazy`](/prompts/rules/react-doctor/react-router-prefer-route-lazy.md): React.lazy creates a route-module waterfall
- [`react-doctor/redux-useselector-inline-derivation`](/prompts/rules/react-doctor/redux-useselector-inline-derivation.md): Select the raw slice in useSelector and derive with useMemo, or hoist into a memoised createSelector from reselect.
- [`react-doctor/redux-useselector-returns-new-collection`](/prompts/rules/react-doctor/redux-useselector-returns-new-collection.md): useSelector that returns a fresh object/array literal re-renders on every action; return a primitive, split into multiple useSelector calls, or pass shallowEqual.
- [`react-doctor/rendering-hoist-jsx`](/prompts/rules/react-doctor/rendering-hoist-jsx.md): Move the static JSX to module scope: `const ICON = <svg>...</svg>` outside the component so it isn't recreated each render
- [`react-doctor/rendering-hydration-no-flicker`](/prompts/rules/react-doctor/rendering-hydration-no-flicker.md): Use a stable server/client snapshot when possible; suppressHydrationWarning cannot prevent a post-paint state update or flicker
- [`react-doctor/rendering-script-defer-async`](/prompts/rules/react-doctor/rendering-script-defer-async.md): Add `defer` for DOM-dependent scripts or `async` for independent ones (analytics). In Next.js, use `<Script strategy="afterInteractive" />` instead
- [`react-doctor/rendering-svg-precision`](/prompts/rules/react-doctor/rendering-svg-precision.md): Truncate path/points/transform decimals to 1–2 digits: sub-pixel precision adds bytes with no visible difference
- [`react-doctor/rendering-usetransition-loading`](/prompts/rules/react-doctor/rendering-usetransition-loading.md): Use useTransition pending state only for work owned by a Transition or Action, not as a replacement for general loading state
- [`react-doctor/rerender-defer-reads-hook`](/prompts/rules/react-doctor/rerender-defer-reads-hook.md): Defer a router read only through an equivalent router snapshot API; window.location is not interchangeable with router state
- [`react-doctor/rerender-derived-state-from-hook`](/prompts/rules/react-doctor/rerender-derived-state-from-hook.md): Use a threshold/media-query hook (e.g. `useMediaQuery("(max-width: 767px)")`): the component re-renders only when the threshold flips, not every pixel
- [`react-doctor/rerender-functional-setstate`](/prompts/rules/react-doctor/rerender-functional-setstate.md): Use the callback form: `setState(prev => prev + 1)` to always read the latest value
- [`react-doctor/rerender-lazy-ref-init`](/prompts/rules/react-doctor/rerender-lazy-ref-init.md): Lazy-init the ref: `const ref = useRef(null); if (ref.current === null) ref.current = expensiveCall()`
- [`react-doctor/rerender-lazy-state-init`](/prompts/rules/react-doctor/rerender-lazy-state-init.md): Pass a pure lazy initializer so expensive setup is skipped on ordinary re-renders
- [`react-doctor/rerender-memo-before-early-return`](/prompts/rules/react-doctor/rerender-memo-before-early-return.md): Review memoized JSX declared before an early return only when profiling shows meaningful wasted work
- [`react-doctor/rerender-memo-with-default-value`](/prompts/rules/react-doctor/rerender-memo-with-default-value.md): Move to module scope: `const EMPTY_ITEMS: Item[] = []` then use as the default value
- [`react-doctor/rerender-state-only-in-handlers`](/prompts/rules/react-doctor/rerender-state-only-in-handlers.md): Replace useState with useRef when the value is only mutated and never read in render: `ref.current = ...` updates without re-rendering the component
- [`react-doctor/rerender-transitions-scroll`](/prompts/rules/react-doctor/rerender-transitions-scroll.md): Throttle high-frequency event state commits with requestAnimationFrame or an appropriate rate limiter; transitions change priority, not event frequency
- [`react-doctor/tanstack-start-loader-parallel-fetch`](/prompts/rules/react-doctor/tanstack-start-loader-parallel-fetch.md): Use `const [a, b] = await Promise.all([fetchA(), fetchB()])` to avoid request waterfalls in route loaders
- [`react-doctor/three-cap-device-pixel-ratio`](/prompts/rules/react-doctor/three-cap-device-pixel-ratio.md): Unbounded Three.js device pixel ratio
- [`react-doctor/three-limit-shadowed-point-lights`](/prompts/rules/react-doctor/three-limit-shadowed-point-lights.md): Too many shadow-casting Three.js point lights
- [`react-doctor/three-no-allocation-in-pointer-move`](/prompts/rules/react-doctor/three-no-allocation-in-pointer-move.md): Three.js allocation inside pointer-move handler
- [`react-doctor/three-no-clone-in-animation-loop`](/prompts/rules/react-doctor/three-no-clone-in-animation-loop.md): Three.js clone inside animation loop
- [`react-doctor/three-no-material-recompile-in-animation-loop`](/prompts/rules/react-doctor/three-no-material-recompile-in-animation-loop.md): Material recompiled inside animation loop
- [`react-doctor/three-no-new-in-animation-loop`](/prompts/rules/react-doctor/three-no-new-in-animation-loop.md): Allocation inside Three.js animation loop
- [`react-doctor/three-no-object-construction-in-render`](/prompts/rules/react-doctor/three-no-object-construction-in-render.md): Three.js object constructed during React render
- [`react-doctor/three-no-redundant-uniforms-need-update`](/prompts/rules/react-doctor/three-no-redundant-uniforms-need-update.md): Redundant uniformsNeedUpdate inside animation loop
- [`react-doctor/three-no-state-in-animation-loop`](/prompts/rules/react-doctor/three-no-state-in-animation-loop.md): React state update inside Three.js animation loop
- [`react-doctor/three-no-state-in-pointer-move`](/prompts/rules/react-doctor/three-no-state-in-pointer-move.md): React state update inside Three.js pointer-move handler
- [`react-doctor/three-require-owned-geometry-cleanup`](/prompts/rules/react-doctor/three-require-owned-geometry-cleanup.md): Locally owned Three.js geometry is not disposed
- [`react-doctor/three-require-owned-material-cleanup`](/prompts/rules/react-doctor/three-require-owned-material-cleanup.md): Locally owned Three.js material is not disposed
- [`react-doctor/three-require-owned-texture-cleanup`](/prompts/rules/react-doctor/three-require-owned-texture-cleanup.md): Locally owned Three.js texture is not disposed
- [`react-doctor/three-shader-no-inverse-of-uniform`](/prompts/rules/react-doctor/three-shader-no-inverse-of-uniform.md): Shader inverts a uniform matrix per invocation
- [`react-doctor/three-shader-no-redundant-frag-depth`](/prompts/rules/react-doctor/three-shader-no-redundant-frag-depth.md): Shader redundantly writes fragment depth
- [`react-doctor/three-shader-prefer-small-integer-pow`](/prompts/rules/react-doctor/three-shader-prefer-small-integer-pow.md): Shader uses pow for a small integer exponent
- [`react-doctor/three-shader-prefer-squared-distance-comparison`](/prompts/rules/react-doctor/three-shader-prefer-squared-distance-comparison.md): Shader compares a computed distance
- [`react-doctor/webgl-no-sync-readback-in-animation-loop`](/prompts/rules/react-doctor/webgl-no-sync-readback-in-animation-loop.md): Synchronous GPU readback inside animation loop
- [`react-doctor/zustand-no-fresh-selector-result`](/prompts/rules/react-doctor/zustand-no-fresh-selector-result.md): Zustand selector returns a fresh value

## Preact

- [`react-doctor/preact-no-children-length`](/prompts/rules/react-doctor/preact-no-children-length.md): Wrap with toChildArray(children) from preact before reading .length or calling array methods on props.children.
- [`react-doctor/preact-no-react-hooks-import`](/prompts/rules/react-doctor/preact-no-react-hooks-import.md): Import hooks from `preact/hooks` (or `preact/compat`), not `react`: import { useState } from "preact/hooks"
- [`react-doctor/preact-no-render-arguments`](/prompts/rules/react-doctor/preact-no-render-arguments.md): Drop render's positional params and read this.props / this.state inside render() instead
- [`react-doctor/preact-prefer-ondblclick`](/prompts/rules/react-doctor/preact-prefer-ondblclick.md): Rename onDoubleClick to onDblClick on host elements: <li onDblClick={openInline}>: Preact uses DOM event names
- [`react-doctor/preact-prefer-oninput`](/prompts/rules/react-doctor/preact-prefer-oninput.md): Replace onChange with onInput on text-like inputs: onInput={(e) => setQuery(e.currentTarget.value)}

## React Compiler

- [`react-hooks-js/component-hook-factories`](/prompts/rules/react-hooks-js/component-hook-factories.md) **(retired)**: Retired validation retained as a deprecated no-op export in eslint-plugin-react-hooks 7.1.1 for configuration compatibility
- [`react-hooks-js/error-boundaries`](/prompts/rules/react-hooks-js/error-boundaries.md): Validates usage of error boundaries instead of try/catch for errors in child components
- [`react-hooks-js/globals`](/prompts/rules/react-hooks-js/globals.md): 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/hooks`](/prompts/rules/react-hooks-js/hooks.md): Validates the rules of hooks
- [`react-hooks-js/immutability`](/prompts/rules/react-hooks-js/immutability.md): 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-library`](/prompts/rules/react-hooks-js/incompatible-library.md): Validates against usage of libraries which are incompatible with memoization (manual or automatic)
- [`react-hooks-js/preserve-manual-memoization`](/prompts/rules/react-hooks-js/preserve-manual-memoization.md): Validate that React Compiler can preserve or exceed the observable guarantees of existing manual memoization
- [`react-hooks-js/purity`](/prompts/rules/react-hooks-js/purity.md): 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/refs`](/prompts/rules/react-hooks-js/refs.md): 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-effect`](/prompts/rules/react-hooks-js/set-state-in-effect.md): 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-render`](/prompts/rules/react-hooks-js/set-state-in-render.md): Validates against setting state during render, which can trigger additional renders and potential infinite render loops
- [`react-hooks-js/static-components`](/prompts/rules/react-hooks-js/static-components.md): 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/todo`](/prompts/rules/react-hooks-js/todo.md): Unimplemented features
- [`react-hooks-js/unsupported-syntax`](/prompts/rules/react-hooks-js/unsupported-syntax.md): Validates against syntax that we do not plan to support in React Compiler
- [`react-hooks-js/use-memo`](/prompts/rules/react-hooks-js/use-memo.md): 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-memo`](/prompts/rules/react-hooks-js/void-use-memo.md): 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

- [`react-doctor/expo-no-non-inlined-env`](/prompts/rules/react-doctor/expo-no-non-inlined-env.md): Use static dotted access: process.env.EXPO_PUBLIC_NAME (computed/destructured reads aren't inlined and are undefined at runtime)
- [`react-doctor/rn-animate-layout-property`](/prompts/rules/react-doctor/rn-animate-layout-property.md) **(retired)**: Retired guidance: do not rewrite layout animation to transforms without checking sibling layout, hit areas, clipping, and text rendering
- [`react-doctor/rn-animation-reaction-as-derived`](/prompts/rules/react-doctor/rn-animation-reaction-as-derived.md): Use a derived value only when the destination shared value has one writer and no identity-sensitive consumers
- [`react-doctor/rn-bottom-sheet-prefer-native`](/prompts/rules/react-doctor/rn-bottom-sheet-prefer-native.md): Choose native form-sheet navigation only when its platform and detent APIs match the product; keep a cross-platform sheet for complex continuous gestures
- [`react-doctor/rn-detox-missing-await`](/prompts/rules/react-doctor/rn-detox-missing-await.md): Prepend await to the Detox action, waitFor chain, or expect(element(...)) assertion: await element(by.id('submit')).tap()
- [`react-doctor/rn-list-callback-per-row`](/prompts/rules/react-doctor/rn-list-callback-per-row.md): Stabilize per-row callbacks only when they cross a memoized row boundary and profiling shows the allocation or rerender matters
- [`react-doctor/rn-list-data-mapped`](/prompts/rules/react-doctor/rn-list-data-mapped.md): Stabilize an inline list projection only when its source is stable and measurement shows repeated transformation or list invalidation
- [`react-doctor/rn-list-missing-estimated-item-size`](/prompts/rules/react-doctor/rn-list-missing-estimated-item-size.md): Add a measured size hint only for FlashList v1 or LegendList v2; current FlashList v2 and LegendList v3 deprecate or ignore these props
- [`react-doctor/rn-list-recyclable-without-types`](/prompts/rules/react-doctor/rn-list-recyclable-without-types.md): Add `getItemType={item => item.kind}` so FlashList keeps separate recycle pools per item type: heterogeneous rows shouldn't share recycled cells
- [`react-doctor/rn-no-deep-imports`](/prompts/rules/react-doctor/rn-no-deep-imports.md): Import from the "react-native" package root, not the deprecated "react-native/Libraries/..." subpath: import { Alert } from "react-native"
- [`react-doctor/rn-no-deprecated-modules`](/prompts/rules/react-doctor/rn-no-deprecated-modules.md): Check the installed React Native version before migrating a deprecated core API; deprecation does not always mean removal
- [`react-doctor/rn-no-dimensions-get`](/prompts/rules/react-doctor/rn-no-dimensions-get.md): Use `const { width, height } = useWindowDimensions()`: it updates reactively on rotation and resize
- [`react-doctor/rn-no-falsy-and-render`](/prompts/rules/react-doctor/rn-no-falsy-and-render.md): Guard numeric-looking conditions: {count > 0 && <X/>}, {Boolean(count) && <X/>}, or {count ? <X/> : null}
- [`react-doctor/rn-no-image-children`](/prompts/rules/react-doctor/rn-no-image-children.md): Replace <Image> with <ImageBackground> to render content over the image: <ImageBackground source={src}>...children...</ImageBackground>
- [`react-doctor/rn-no-inline-flatlist-renderitem`](/prompts/rules/react-doctor/rn-no-inline-flatlist-renderitem.md): Stabilize renderItem only when it affects a measured list bailout; preserve every captured dependency
- [`react-doctor/rn-no-inline-object-in-list-item`](/prompts/rules/react-doctor/rn-no-inline-object-in-list-item.md): Hoist an inline row object only when it crosses a memoized boundary or profiling identifies allocation pressure
- [`react-doctor/rn-no-legacy-expo-packages`](/prompts/rules/react-doctor/rn-no-legacy-expo-packages.md): Migrate a legacy Expo package through its replacement lifecycle and API, not by changing only the import
- [`react-doctor/rn-no-legacy-shadow-styles`](/prompts/rules/react-doctor/rn-no-legacy-shadow-styles.md): Compatibility-only duplicate of rn-style-prefer-boxshadow; do not rewrite valid platform shadow fallbacks from this finding
- [`react-doctor/rn-no-non-native-navigator`](/prompts/rules/react-doctor/rn-no-non-native-navigator.md): Use `@react-navigation/native-stack` (or `native-tabs` in v7+) for platform-native transitions and gestures
- [`react-doctor/rn-no-panresponder`](/prompts/rules/react-doctor/rn-no-panresponder.md): Consider Gesture Handler for a measured PanResponder limitation after dependency, root, and responder-semantics checks
- [`react-doctor/rn-no-raw-text`](/prompts/rules/react-doctor/rn-no-raw-text.md): Wrap text in a `<Text>` component: `<Text>{value}</Text>`: raw strings outside `<Text>` crash on React Native
- [`react-doctor/rn-no-renderitem-key`](/prompts/rules/react-doctor/rn-no-renderitem-key.md): Remove the no-op `key` from the JSX row that renderItem returns and set `keyExtractor` (or `item.key`) on the list instead.
- [`react-doctor/rn-no-scroll-state`](/prompts/rules/react-doctor/rn-no-scroll-state.md): Keep per-frame scroll data out of React state while preserving discrete state updates and one-shot latches
- [`react-doctor/rn-no-scrollview-mapped-list`](/prompts/rules/react-doctor/rn-no-scrollview-mapped-list.md): Use FlashList, LegendList, or FlatList: `<ScrollView>{items.map(...)}</ScrollView>` mounts every row in memory
- [`react-doctor/rn-no-set-native-props`](/prompts/rules/react-doctor/rn-no-set-native-props.md): Do not auto-rewrite setNativeProps: it remains supported under Fabric and can be appropriate for TextInput or measured high-frequency updates
- [`react-doctor/rn-no-single-element-style-array`](/prompts/rules/react-doctor/rn-no-single-element-style-array.md): Use `style={value}` instead of `style={[value]}`: single-element arrays add unnecessary allocation
- [`react-doctor/rn-prefer-content-inset-adjustment`](/prompts/rules/react-doctor/rn-prefer-content-inset-adjustment.md) **(retired)**: Retired guidance: contentInsetAdjustmentBehavior is iOS-specific and cannot generally replace shared safe-area handling
- [`react-doctor/rn-prefer-expo-image`](/prompts/rules/react-doctor/rn-prefer-expo-image.md): In an Expo-managed file, consider expo-image for non-static Image or ImageBackground usage while preserving layout and overlay semantics
- [`react-doctor/rn-prefer-pressable`](/prompts/rules/react-doctor/rn-prefer-pressable.md): Use `<Pressable>` from react-native (or react-native-gesture-handler) instead of legacy Touchable* components
- [`react-doctor/rn-prefer-pressable-over-gesture-detector`](/prompts/rules/react-doctor/rn-prefer-pressable-over-gesture-detector.md): Use Pressable for a simple tap only when its press lifecycle preserves the Gesture.Tap callbacks and configuration
- [`react-doctor/rn-prefer-reanimated`](/prompts/rules/react-doctor/rn-prefer-reanimated.md): Consider Reanimated for a measured RN Animated or LayoutAnimation limitation after version and architecture checks
- [`react-doctor/rn-pressable-shared-value-mutation`](/prompts/rules/react-doctor/rn-pressable-shared-value-mutation.md): Review measured press latency without replacing an accessible Pressable with a tap-only GestureDetector by default
- [`react-doctor/rn-scrollview-dynamic-padding`](/prompts/rules/react-doctor/rn-scrollview-dynamic-padding.md): Consider an iOS contentInset path for measured dynamic-padding reflow while preserving Android, web, and indicator behavior
- [`react-doctor/rn-scrollview-flex-in-content-container`](/prompts/rules/react-doctor/rn-scrollview-flex-in-content-container.md): Replace `flex: <positive number>` on contentContainerStyle with `flexGrow: 1`
- [`react-doctor/rn-style-prefer-boxshadow`](/prompts/rules/react-doctor/rn-style-prefer-boxshadow.md): Use boxShadow only when RN 0.76+, the New Architecture, and target Android API levels support the required shadow

## Security

- [`react-doctor/active-static-asset`](/prompts/rules/react-doctor/active-static-asset.md): 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-risk`](/prompts/rules/react-doctor/agent-tool-capability-risk.md): 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-surface`](/prompts/rules/react-doctor/artifact-baas-authority-surface.md): 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-leak`](/prompts/rules/react-doctor/artifact-env-leak.md): 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-leak`](/prompts/rules/react-doctor/artifact-secret-leak.md): 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-storage`](/prompts/rules/react-doctor/auth-token-in-web-storage.md): Store authentication credentials in server-set `HttpOnly` cookies instead of browser storage, where cross-site scripting can read them.
- [`react-doctor/build-pipeline-secret-boundary`](/prompts/rules/react-doctor/build-pipeline-secret-boundary.md): 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-risk`](/prompts/rules/react-doctor/clickjacking-redirect-risk.md): 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-risk`](/prompts/rules/react-doctor/command-execution-input-risk.md): 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-risk`](/prompts/rules/react-doctor/cors-cookie-trust-risk.md): 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-sink`](/prompts/rules/react-doctor/dangerous-html-sink.md): 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-field`](/prompts/rules/react-doctor/firebase-client-owned-authz-field.md): 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-rules`](/prompts/rules/react-doctor/firebase-permissive-rules.md): 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-auth`](/prompts/rules/react-doctor/firebase-query-filter-as-auth.md): 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-risk`](/prompts/rules/react-doctor/git-provider-url-injection-risk.md): 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-sandbox`](/prompts/rules/react-doctor/iframe-missing-sandbox.md): Add sandbox="" (or a curated, minimal set of allow- tokens) to your iframe to restrict embedded content.
- [`react-doctor/import-metadata-execution-risk`](/prompts/rules/react-doctor/import-metadata-execution-risk.md): Evaluating imported metadata or file contents (EXIF, manifests, presets, uploads, archives) as code lets an attacker achieve remote code execution.
- [`react-doctor/insecure-crypto-risk`](/prompts/rules/react-doctor/insecure-crypto-risk.md): 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-cookie`](/prompts/rules/react-doctor/insecure-session-cookie.md): Auth cookie missing HttpOnly protection
- [`react-doctor/jsx-no-target-blank`](/prompts/rules/react-doctor/jsx-no-target-blank.md): Add rel="noreferrer" (or "noopener") whenever using target="_blank".
- [`react-doctor/jwt-insecure-verification`](/prompts/rules/react-doctor/jwt-insecure-verification.md): JWT verified with the 'none' algorithm
- [`react-doctor/key-lifecycle-risk`](/prompts/rules/react-doctor/key-lifecycle-risk.md): 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-risk`](/prompts/rules/react-doctor/local-rpc-native-bridge-risk.md): 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-risk`](/prompts/rules/react-doctor/mcp-tool-capability-risk.md): 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-risk`](/prompts/rules/react-doctor/mdx-ssr-execution-risk.md): 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-handler`](/prompts/rules/react-doctor/nextjs-no-side-effect-in-get-handler.md): 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-eval`](/prompts/rules/react-doctor/no-eval.md): Use `JSON.parse` for serialized data, `Function(...)` (still careful) for trusted templates, or refactor to avoid dynamic code execution
- [`react-doctor/no-path-prefix-containment`](/prompts/rules/react-doctor/no-path-prefix-containment.md): Path containment check uses a string prefix
- [`react-doctor/no-secrets-in-client-code`](/prompts/rules/react-doctor/no-secrets-in-client-code.md): Move secrets to server-only code. Public client environment variables are bundled into browser code and must not contain secrets
- [`react-doctor/nosql-injection-risk`](/prompts/rules/react-doctor/nosql-injection-risk.md): 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-secret`](/prompts/rules/react-doctor/package-metadata-secret.md): 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-risk`](/prompts/rules/react-doctor/path-traversal-risk.md): 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-risk`](/prompts/rules/react-doctor/plugin-update-trust-risk.md): Downloading and running an update or plugin without verifying its integrity lets an attacker ship malicious code to your users.
- [`react-doctor/postmessage-origin-risk`](/prompts/rules/react-doctor/postmessage-origin-risk.md): 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-artifact`](/prompts/rules/react-doctor/public-debug-artifact.md): 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-name`](/prompts/rules/react-doctor/public-env-secret-name.md): 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-risk`](/prompts/rules/react-doctor/raw-sql-injection-risk.md): 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-html`](/prompts/rules/react-doctor/react-markdown-unsanitized-raw-html.md): Unsanitized raw HTML in React Markdown
- [`react-doctor/react-router-csp-nonce-consistency`](/prompts/rules/react-doctor/react-router-csp-nonce-consistency.md): CSP nonce is not shared across server rendering
- [`react-doctor/repository-secret-file`](/prompts/rules/react-doctor/repository-secret-file.md): 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-assignment`](/prompts/rules/react-doctor/request-body-mass-assignment.md): Request input spread without field allowlist
- [`react-doctor/require-pnpm-hardening`](/prompts/rules/react-doctor/require-pnpm-hardening.md): 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-fallback`](/prompts/rules/react-doctor/secret-in-fallback.md): Hardcoded secret fallback for env var
- [`react-doctor/supabase-client-owned-authz-field`](/prompts/rules/react-doctor/supabase-client-owned-authz-field.md): 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-risk`](/prompts/rules/react-doctor/supabase-rls-policy-risk.md): 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-rls`](/prompts/rules/react-doctor/supabase-table-missing-rls.md): Supabase table created without Row Level Security
- [`react-doctor/svg-filter-clickjacking-risk`](/prompts/rules/react-doctor/svg-filter-clickjacking-risk.md): 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-mutation`](/prompts/rules/react-doctor/tanstack-start-get-mutation.md): 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-loader`](/prompts/rules/react-doctor/tanstack-start-no-secrets-in-loader.md): Loaders are isomorphic (run on both server and client). Wrap secret access in `createServerFn()` so it stays server-only
- [`react-doctor/tenant-static-proxy-risk`](/prompts/rules/react-doctor/tenant-static-proxy-risk.md): 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-html`](/prompts/rules/react-doctor/unsafe-json-in-html.md): Unescaped JSON in HTML or script sink
- [`react-doctor/untrusted-redirect-following`](/prompts/rules/react-doctor/untrusted-redirect-following.md): 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-action`](/prompts/rules/react-doctor/url-prefilled-privileged-action.md): 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-risk`](/prompts/rules/react-doctor/webhook-signature-risk.md): 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-noopener`](/prompts/rules/react-doctor/window-open-without-noopener.md): window.open without noopener
- [`socket/low-supply-chain-score`](/prompts/rules/socket/low-supply-chain-score.md): 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

- [`react-doctor/server-after-nonblocking`](/prompts/rules/react-doctor/server-after-nonblocking.md): `import { after } from 'next/server'` then wrap: `after(() => analytics.track(...))`: response isn't blocked
- [`react-doctor/server-auth-actions`](/prompts/rules/react-doctor/server-auth-actions.md): Add `const session = await auth()` at the top and throw/redirect if unauthorized before any data access
- [`react-doctor/server-cache-with-object-literal`](/prompts/rules/react-doctor/server-cache-with-object-literal.md): Pass primitives to React.cache()-wrapped functions: argument identity (not deep equality) is the dedup key, so a fresh `{}` per render bypasses the cache
- [`react-doctor/server-dedup-props`](/prompts/rules/react-doctor/server-dedup-props.md): Pass the source array once and derive the projection on the client: passing both doubles RSC serialization bytes
- [`react-doctor/server-fetch-without-revalidate`](/prompts/rules/react-doctor/server-fetch-without-revalidate.md): Pass `{ next: { revalidate: <seconds> } }` (or `cache: "no-store"` / `next: { tags: [...] }`) so stale cached data doesn't silently persist
- [`react-doctor/server-hoist-static-io`](/prompts/rules/react-doctor/server-hoist-static-io.md): Hoist the read to module scope: `const FONT_DATA = await fetch(new URL('./fonts/Inter.ttf', import.meta.url)).then(r => r.arrayBuffer())` runs once at module load
- [`react-doctor/server-no-mutable-module-state`](/prompts/rules/react-doctor/server-no-mutable-module-state.md): Move per-request data into the action body, headers/cookies, or a request-scope (React.cache, AsyncLocalStorage). Module-scope `let`/`var` is shared across requests.
- [`react-doctor/server-sequential-independent-await`](/prompts/rules/react-doctor/server-sequential-independent-await.md): Wrap independent awaits in `Promise.all([...])` so they race instead of waterfalling: second call doesn't depend on the first

## State & Effects

- [`react-doctor/activity-wraps-effect-heavy-subtree`](/prompts/rules/react-doctor/activity-wraps-effect-heavy-subtree.md): 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-cleanup`](/prompts/rules/react-doctor/effect-needs-cleanup.md): 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-deps`](/prompts/rules/react-doctor/hooks-no-nan-in-deps.md): 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-object`](/prompts/rules/react-doctor/jotai-derived-atom-returns-fresh-object.md): 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-body`](/prompts/rules/react-doctor/jotai-select-atom-in-render-body.md): Lift selectAtom to module scope, or wrap it: const a = useMemo(() => selectAtom(base, fn), [deps])
- [`react-doctor/jotai-tq-use-raw-query-atom`](/prompts/rules/react-doctor/jotai-tq-use-raw-query-atom.md): Derive the field once, then subscribe to the derived atom: const dataAtom = atom((get) => get(queryAtom).data)
- [`react-doctor/no-cascading-set-state`](/prompts/rules/react-doctor/no-cascading-set-state.md) **(retired)**: Combine into useReducer: `const [state, dispatch] = useReducer(reducer, initialState)`
- [`react-doctor/no-derived-state`](/prompts/rules/react-doctor/no-derived-state.md): Disallow storing derived state in an effect.
- [`react-doctor/no-derived-state-effect`](/prompts/rules/react-doctor/no-derived-state-effect.md): 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-useState`](/prompts/rules/react-doctor/no-derived-useState.md): Remove useState and compute the value inline: `const value = transform(propName)`
- [`react-doctor/no-did-mount-set-state`](/prompts/rules/react-doctor/no-did-mount-set-state.md): 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-state`](/prompts/rules/react-doctor/no-did-update-set-state.md): Avoid calling this.setState in componentDidUpdate; derive the value with getDerivedStateFromProps to prevent re-render loops
- [`react-doctor/no-direct-state-mutation`](/prompts/rules/react-doctor/no-direct-state-mutation.md): 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-chain`](/prompts/rules/react-doctor/no-effect-chain.md): 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-handler`](/prompts/rules/react-doctor/no-effect-event-handler.md): Move the conditional logic into onClick, onChange, or onSubmit handlers directly
- [`react-doctor/no-effect-event-in-deps`](/prompts/rules/react-doctor/no-effect-event-in-deps.md): Call the useEffectEvent callback inside the effect body without listing it; its identity is intentionally unstable
- [`react-doctor/no-effect-with-fresh-deps`](/prompts/rules/react-doctor/no-effect-with-fresh-deps.md): 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-handler`](/prompts/rules/react-doctor/no-event-handler.md): Disallow using state and an effect as an event handler.
- [`react-doctor/no-event-trigger-state`](/prompts/rules/react-doctor/no-event-trigger-state.md): 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-effect`](/prompts/rules/react-doctor/no-fetch-in-effect.md): Use `useQuery()` from @tanstack/react-query, `useSWR()`, or fetch in a Server Component instead
- [`react-doctor/no-initialize-state`](/prompts/rules/react-doctor/no-initialize-state.md): Disallow initializing state in an effect.
- [`react-doctor/no-mirror-prop-effect`](/prompts/rules/react-doctor/no-mirror-prop-effect.md): 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-deps`](/prompts/rules/react-doctor/no-mutable-in-deps.md): 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-state`](/prompts/rules/react-doctor/no-mutating-reducer-state.md): 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-effect`](/prompts/rules/react-doctor/no-prop-callback-in-effect.md): 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-change`](/prompts/rules/react-doctor/no-reset-all-state-on-prop-change.md): Disallow resetting all state in an effect when a prop changes.
- [`react-doctor/no-self-updating-effect`](/prompts/rules/react-doctor/no-self-updating-effect.md): 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-render`](/prompts/rules/react-doctor/no-set-state-in-render.md): 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-state`](/prompts/rules/react-doctor/no-will-update-set-state.md): Don't call this.setState in componentWillUpdate: move the update to getDerivedStateFromProps or componentDidUpdate.
- [`react-doctor/prefer-use-effect-event`](/prompts/rules/react-doctor/prefer-use-effect-event.md): On React 19.2+, use useEffectEvent for non-reactive callbacks called only from an Effect
- [`react-doctor/prefer-use-sync-external-store`](/prompts/rules/react-doctor/prefer-use-sync-external-store.md): Replace a manual external-store subscription with useSyncExternalStore while preserving subscribe receiver semantics and stable snapshots
- [`react-doctor/prefer-useReducer`](/prompts/rules/react-doctor/prefer-useReducer.md): Group related state: `const [state, dispatch] = useReducer(reducer, { field1, field2, ... })`
- [`react-doctor/rerender-dependencies`](/prompts/rules/react-doctor/rerender-dependencies.md): Extract to a useMemo, useRef, or module-level constant so the reference is stable

## TanStack Query

- [`react-doctor/query-destructure-result`](/prompts/rules/react-doctor/query-destructure-result.md): Retired: binding a TanStack Query result object does not defeat tracked-property subscriptions
- [`react-doctor/query-mutation-missing-invalidation`](/prompts/rules/react-doctor/query-mutation-missing-invalidation.md): Add `onSuccess: () => queryClient.invalidateQueries({ queryKey: ['...'] })` so cached data stays in sync after the mutation
- [`react-doctor/query-no-query-in-effect`](/prompts/rules/react-doctor/query-no-query-in-effect.md): React Query manages refetching automatically via queryKey dependencies and the `enabled` option: manual refetch() in useEffect is usually unnecessary
- [`react-doctor/query-no-rest-destructuring`](/prompts/rules/react-doctor/query-no-rest-destructuring.md): Destructure only the fields you need: `const { data, isLoading } = useQuery(...)`: rest destructuring subscribes to all fields and causes extra re-renders
- [`react-doctor/query-no-usequery-for-mutation`](/prompts/rules/react-doctor/query-no-usequery-for-mutation.md): Use `useMutation()` for POST/PUT/DELETE: it provides onSuccess/onError callbacks, doesn't auto-refetch, and correctly models write operations
- [`react-doctor/query-no-void-query-fn`](/prompts/rules/react-doctor/query-no-void-query-fn.md): queryFn must return a value for the cache. Use the `enabled` option to conditionally disable the query instead of returning undefined
- [`react-doctor/query-stable-query-client`](/prompts/rules/react-doctor/query-stable-query-client.md): Move `new QueryClient()` to module scope or wrap in `useState(() => new QueryClient())`: recreating it on every render resets the entire cache

## TanStack Start

- [`react-doctor/tanstack-start-missing-head-content`](/prompts/rules/react-doctor/tanstack-start-missing-head-content.md): Add `<HeadContent />` inside `<head>` in your __root route: without it, route `head()` meta tags are silently dropped
- [`react-doctor/tanstack-start-no-anchor-element`](/prompts/rules/react-doctor/tanstack-start-no-anchor-element.md): `import { Link } from '@tanstack/react-router'`: enables type-safe routes, preloading via `preload="intent"`, and client-side navigation
- [`react-doctor/tanstack-start-no-direct-fetch-in-loader`](/prompts/rules/react-doctor/tanstack-start-no-direct-fetch-in-loader.md): Use `createServerFn()` from @tanstack/react-start: provides type-safe RPC, input validation, and proper server/client code splitting
- [`react-doctor/tanstack-start-no-dynamic-server-fn-import`](/prompts/rules/react-doctor/tanstack-start-no-dynamic-server-fn-import.md): Use `import { myFn } from '~/utils/my.functions'`: the bundler replaces server code with RPC stubs only for static imports
- [`react-doctor/tanstack-start-no-navigate-in-render`](/prompts/rules/react-doctor/tanstack-start-no-navigate-in-render.md): Use `throw redirect({ to: '/path' })` in `beforeLoad` or `loader` instead: navigate() during render causes hydration issues
- [`react-doctor/tanstack-start-no-use-server-in-handler`](/prompts/rules/react-doctor/tanstack-start-no-use-server-in-handler.md): TanStack Start handles server boundaries automatically via the Vite plugin: "use server" inside createServerFn causes compilation errors
- [`react-doctor/tanstack-start-no-useeffect-fetch`](/prompts/rules/react-doctor/tanstack-start-no-useeffect-fetch.md): Fetch data in the route `loader` instead: the router coordinates loading before rendering to avoid waterfalls
- [`react-doctor/tanstack-start-redirect-in-try-catch`](/prompts/rules/react-doctor/tanstack-start-redirect-in-try-catch.md): TanStack Router's `redirect()` and `notFound()` throw special errors caught by the router. Move them outside the try block or re-throw in the catch
- [`react-doctor/tanstack-start-route-property-order`](/prompts/rules/react-doctor/tanstack-start-route-property-order.md): Follow the order: params/validateSearch → loaderDeps → context → beforeLoad → loader → head. See https://tanstack.com/router/latest/docs/eslint/create-route-property-order
- [`react-doctor/tanstack-start-server-fn-method-order`](/prompts/rules/react-doctor/tanstack-start-server-fn-method-order.md): Chain methods in order: .middleware() → .inputValidator() → .client() → .server() → .handler(): types depend on this sequence
- [`react-doctor/tanstack-start-server-fn-validate-input`](/prompts/rules/react-doctor/tanstack-start-server-fn-validate-input.md): Add `.inputValidator(schema)` before `.handler()`: data crosses a network boundary and must be validated at runtime
