Rules reference

413 engine rules grouped by category. Each rule pairs a validation prompt (when to confirm vs. suppress a finding) with a fix prompt (how to address it).

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

Accessibility

Architecture

Bundle Size

Correctness

Dead Code

  • deslop/circular-dependency: Disallow runtime import cycles between modules (A imports B ... imports A).
  • deslop/feature-flag: Disallow stale feature flags that permanently guard dead code.
  • deslop/misclassified-dependency: Move a runtime dep consumed only via `import type` into devDependencies.
  • deslop/re-export-cycle: Disallow import cycles formed by re-export statements (export * / export { } from).
  • deslop/unused-class-member: Flag a public/protected class method, property, or accessor that is declared but never referenced anywhere in the codebase.
  • deslop/unused-dependency: Flag a package.json dependencies entry that no scanned source file imports as an unused dependency (deslop detectStalePackages, UnusedDependency name/isDevDependency=false).
  • deslop/unused-dev-dependency: Flag a devDependencies entry (isDevDependency=true) never imported by any scanned source file.
  • deslop/unused-enum-member: Flag an enum member that is declared but never referenced anywhere in the project (e.g. `enum Color { Red, Green, Blue }` where `Color.Blue` is never used).
  • deslop/unused-export: Flag a named or default value export (isTypeOnly=false) that no other module in the project imports.
  • deslop/unused-file: Flag a source file unreachable from any configured entry point (deslop unusedFiles / detectOrphanFiles), likely dead — delete it.
  • deslop/unused-type: Disallow exporting a type/interface that no other module imports (opt-in, requires reportTypes).
  • deslop/unused-type-declaration: Disallow exported type declarations (interface / type-alias / enum-type) that are never referenced anywhere in the project.

Next.js

Performance

Preact

React Compiler

  • react-hooks-js/component-hook-factories: Deprecated: this rule has been removed in 7.1.0.
  • react-hooks-js/error-boundaries: Validates usage of error boundaries instead of try/catch for errors in child components
  • react-hooks-js/globals: 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: Validates the rules of hooks
  • react-hooks-js/immutability: 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: Validates against usage of libraries which are incompatible with memoization (manual or automatic)
  • react-hooks-js/preserve-manual-memoization: Validates that existing manual memoized is preserved by the compiler. React Compiler will only compile components and hooks if its inference [matches or exceeds the existing manual memoization](https://react.dev/learn/react-compiler/introduction#what-should-i-do-about-usememo-usecallback-and-reactmemo)
  • react-hooks-js/purity: 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: 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: 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: Validates against setting state during render, which can trigger additional renders and potential infinite render loops
  • react-hooks-js/static-components: 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: Unimplemented features
  • react-hooks-js/unsupported-syntax: Validates against syntax that we do not plan to support in React Compiler
  • react-hooks-js/use-memo: 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: 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

Security

  • react-doctor/active-static-asset: 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: 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: 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: 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: 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/build-pipeline-secret-boundary: 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: 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: 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: 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: 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: 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: 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: 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: 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: Add sandbox="" (or a curated, minimal set of allow- tokens) to your iframe to restrict embedded content.
  • react-doctor/import-metadata-execution-risk: Evaluating imported metadata or file contents (EXIF, manifests, presets, uploads, archives) as code lets an attacker achieve remote code execution.
  • react-doctor/insecure-crypto-risk: Weak primitives (MD5, SHA-1, DES, RC4), non-timing-safe comparisons, or `Math.random()` for security values make signatures, tokens, and passwords easier to forge or guess.
  • react-doctor/jsx-no-target-blank: Add rel="noreferrer" (or "noopener") whenever using target="_blank".
  • react-doctor/key-lifecycle-risk: A private key or release credential committed inline to the repo is exposed in git history and must be rotated and revoked.
  • react-doctor/local-rpc-native-bridge-risk: A localhost or native bridge that accepts loose origins and exposes install/update or shell commands lets a malicious web page drive native actions on the user's machine.
  • react-doctor/mcp-tool-capability-risk: An MCP tool runs with the connecting client's authority, so reaching shell, filesystem, or network primitives without validation lets injected input abuse them.
  • react-doctor/mdx-ssr-execution-risk: Compiling untrusted MDX with the full pipeline runs attacker-supplied JSX and expressions on your server, which can lead to code execution.
  • react-doctor/nextjs-no-side-effect-in-get-handler: 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: Use `JSON.parse` for serialized data, `Function(...)` (still careful) for trusted templates, or refactor to avoid dynamic code execution
  • react-doctor/no-secrets-in-client-code: 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: 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: 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: 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: 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: 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: 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: 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: 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/repository-secret-file: 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/require-pnpm-hardening: pnpm project is missing supply-chain hardening in pnpm-workspace.yaml — set `minimumReleaseAge`, keep `blockExoticSubdeps: true`, and set `trustPolicy: no-downgrade`
  • react-doctor/supabase-client-owned-authz-field: 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: 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/svg-filter-clickjacking-risk: Applying CSS or SVG filters over a cross-origin iframe can be used for clickjacking or to read pixels from framed content the attacker should not see.
  • react-doctor/tanstack-start-get-mutation: 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: 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: Building an asset path from a client-supplied tenant, subdomain, or workspace value lets one tenant read another tenant's files.
  • react-doctor/untrusted-redirect-following: 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: 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: An inbound webhook handler that acts on the request body without verifying the provider's signature will process forged requests from anyone.
  • socket/low-supply-chain-score: 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

State & Effects

  • effect/no-adjust-state-on-prop-change: Disallow adjusting state in an effect when a prop changes.
  • effect/no-chain-state-updates: Disallow chaining state changes in an effect.
  • effect/no-derived-state: Disallow storing derived state in an effect.
  • effect/no-event-handler: Disallow using state and an effect as an event handler.
  • effect/no-initialize-state: Disallow initializing state in an effect.
  • effect/no-pass-data-to-parent: Disallow passing data to parents in an effect.
  • effect/no-pass-live-state-to-parent: Disallow passing live state to parents in an effect.
  • effect/no-reset-all-state-on-prop-change: Disallow resetting all state in an effect when a prop changes.
  • react-doctor/activity-wraps-effect-heavy-subtree: Audit the `<Activity>` subtree: every hide/show cycle tears down and recreates every `useEffect`/`useLayoutEffect` inside, so move subscriptions and effect-driven setState chains outside the boundary or pre-resolve the data above it.
  • react-doctor/effect-needs-cleanup: 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: Remove the literal NaN from the dependency array, or normalise it (Number.isNaN(x) ? 0 : x) before passing it in.
  • react-doctor/jotai-derived-atom-returns-fresh-object: 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: Lift selectAtom to module scope, or wrap it: const a = useMemo(() => selectAtom(base, fn), [deps])
  • react-doctor/jotai-tq-use-raw-query-atom: Derive the field once, then subscribe to the derived atom: const dataAtom = atom((get) => get(queryAtom).data)
  • react-doctor/no-cascading-set-state: Combine into useReducer: `const [state, dispatch] = useReducer(reducer, initialState)`
  • react-doctor/no-derived-state-effect: 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-use: Don't pass a promise created during render into use(); create it in a Server Component (or a stable cache) so its reference stays stable across renders
  • react-doctor/no-derived-useState: Remove useState and compute the value inline: `const value = transform(propName)`
  • react-doctor/no-did-mount-set-state: 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: Avoid calling this.setState in componentDidUpdate; derive the value with getDerivedStateFromProps to prevent re-render loops
  • react-doctor/no-direct-state-mutation: 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: 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: Move the conditional logic into onClick, onChange, or onSubmit handlers directly
  • react-doctor/no-effect-event-in-deps: Call the useEffectEvent callback inside the effect body without listing it; its identity is intentionally unstable
  • react-doctor/no-effect-with-fresh-deps: 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-trigger-state: 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: Use `useQuery()` from @tanstack/react-query, `useSWR()`, or fetch in a Server Component instead
  • react-doctor/no-mirror-prop-effect: 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: 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: 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: Lift the shared state into a Provider so both sides read the same source — no useEffect-driven sync needed
  • react-doctor/no-self-updating-effect: 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: 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: Don't call this.setState in componentWillUpdate — move the update to getDerivedStateFromProps or componentDidUpdate.
  • react-doctor/prefer-use: Replace useContext(Context) with the React 19 use(Context) API, which reads the same value but may be called conditionally
  • react-doctor/prefer-use-effect-event: Wrap the callback with `useEffectEvent(callback)` (React 19+) and call the resulting binding from inside the sub-handler. The Effect Event captures the latest props/state without being a reactive dep, so the effect doesn't re-subscribe on every parent render. See https://react.dev/reference/react/useEffectEvent
  • react-doctor/prefer-use-sync-external-store: Replace the `useState(getSnapshot())` + `useEffect(() => store.subscribe(() => setSnapshot(getSnapshot())))` pair with `useSyncExternalStore(store.subscribe, getSnapshot)`. The hook handles tearing during concurrent renders and SSR snapshots; the manual subscribe pattern doesn't
  • react-doctor/prefer-useReducer: Group related state: `const [state, dispatch] = useReducer(reducer, { field1, field2, ... })`
  • react-doctor/rerender-dependencies: Extract to a useMemo, useRef, or module-level constant so the reference is stable

TanStack Query

TanStack Start