What are the rules of React hooks?
TL;DR
React hooks have a few essential rules to ensure they work correctly. Always call hooks at the top level of your component or custom hook — never inside loops, conditions, nested functions, or after an early return. Only call hooks from React function components or other custom hooks (whose names must start with use). Lean on eslint-plugin-react-hooks to enforce these rules. The React Compiler (RC/stable by 2026) relaxes the need for some manual memoization, but the rules of hooks themselves still apply.
What are the rules of React hooks?
Always call hooks at the top level
Hooks must be called in the same order on every render. That means you cannot call them inside loops, conditions, nested functions, or after an early return. React identifies which useState/useEffect/etc. call corresponds to which piece of state purely by call order — break the order and React's internal bookkeeping desyncs.
// Correctfunction MyComponent({ enabled }) {const [count, setCount] = useState(0);if (!enabled) {// Use the value conditionally — fine.}return <div>{count}</div>;}// Incorrect — hook inside an `if`function MyComponent({ enabled }) {if (enabled) {const [count, setCount] = useState(0); // hook order changes between rendersreturn <div>{count}</div>;}return null;}// Incorrect — hook after an early returnfunction MyComponent({ items }) {if (items.length === 0) return null;const [selected, setSelected] = useState(null); // skipped on the early-return pathreturn <List items={items} selected={selected} onSelect={setSelected} />;}
To fix the early-return case, move the hook above the conditional:
function MyComponent({ items }) {const [selected, setSelected] = useState(null);if (items.length === 0) return null;return <List items={items} selected={selected} onSelect={setSelected} />;}
Only call hooks from React functions
Hooks can only be called from:
- React function components.
- Other custom hooks (which by convention must have a name starting with
use).
Calling a hook from a regular utility function, a class component, or an event handler is not allowed.
// Correct — function componentfunction MyComponent() {const [count, setCount] = useState(0);return <div>{count}</div>;}// Correct — custom hook (name starts with `use`)function useCounter(initial = 0) {const [count, setCount] = useState(initial);const increment = () => setCount((c) => c + 1);return { count, increment };}// Incorrect — plain function, not a component or hookfunction regularFunction() {const [count, setCount] = useState(0); // violates the rules of hooks}
The use prefix isn't cosmetic — it's how the linter identifies a custom hook and enforces the rules of hooks inside it. Naming a function getCounter instead of useCounter will silently disable those checks.
Use eslint-plugin-react-hooks
The eslint-plugin-react-hooks package automates enforcement of these rules. It ships two main rules: react-hooks/rules-of-hooks (call order, where hooks may be called) and react-hooks/exhaustive-deps (correct dependency arrays for useEffect, useMemo, useCallback).
npm install eslint-plugin-react-hooks --save-dev
For an ESLint legacy .eslintrc config:
{"plugins": ["react-hooks"],"rules": {"react-hooks/rules-of-hooks": "error","react-hooks/exhaustive-deps": "warn"}}
For ESLint 9's flat config (eslint.config.js), import the plugin and spread its recommended config:
import reactHooks from 'eslint-plugin-react-hooks';export default [{plugins: { 'react-hooks': reactHooks },rules: reactHooks.configs.recommended.rules,},];
eslint-plugin-react-hooks v5 added flat-config support and ships an additional rule set for the React Compiler when you enable it.
A note on the React Compiler
The React Compiler (RC and on track for stable in 2026) auto-memoizes components and values, removing most of the need for hand-written useMemo and useCallback. It does not change the rules of hooks — your hooks still have to be called unconditionally at the top level, from components or custom hooks. The compiler actually relies on those rules to do its job safely, and will refuse to optimize components that violate them.