Rules of hooks
📚 What are the Rules of Hooks? React hooks (useState, useEffect, useContext, etc.) have two strict rules that you MUST follow. Breaking these rules causes mysterious bugs where state gets mixed up between renders. React actually includes a linter rule (eslint-plugin-react-hooks) to catch violation…

Appy Says…
React Hooks have two golden rules that sound restrictive but make perfect sense once you understand why. Break them and you'll get confusing bugs that are very hard to debug. Follow them and Hooks work flawlessly.
What are the Rules of Hooks?
The two rules that React enforces for all hooks:
- •Rule 1 — Call Hooks at the top level: Never call hooks inside loops, conditions, or nested functions
- •Rule 2 — Call Hooks only from React functions: Only call hooks in function components or custom hooks — not regular JS functions
- •Bad:
if (isLoggedIn) { const [user] = useState(null); } - •Good:
const [user] = useState(null); if (isLoggedIn) { /* use user */ } - •The ESLint plugin
eslint-plugin-react-hooksenforces both rules automatically - •React tracks hooks by their call order — the same hooks must be called every render
Think of it like Minecraft redstone rules
Redstone has rules: power travels exactly 15 blocks; comparators face a specific way; some blocks need to update in a specific order. Break the rules and circuits behave unpredictably. React's hook order is its redstone contract — call them consistently, same order, every render.
How It Works
- •1. React stores hook state in an internal array per component
- •2. On render 1: [state1, state2, state3] — each useState gets slot 0, 1, 2
- •3. On render 2: React reads the same slots in the same order
- •4. If a hook is inside a condition: render 1 may call 3 hooks; render 2 may call 2 — slots misalign
- •5. This causes the wrong state to be read for each hook
- •6. React's lint rule catches this at dev-time before it can cause runtime bugs
Real-World Examples
- •Bad:
if (showSearch) { const [query, setQuery] = useState(''); }— breaks hook order - •Good:
const [query, setQuery] = useState(''); // use only when showSearch is true - •Bad: calling useState inside a forEach callback
- •Good: extract to a custom hook if you need per-item state
Key Facts
- •React 18.2 added a new lint rule for exhaustive-deps — another hook rule for useEffect
- •The rules are enforced by the
eslint-plugin-react-hookspackage (bundled in Create React App and Vite) - •Custom hooks MUST start with 'use' — this is how React identifies them for the linter
- •React Compiler (React 19+) enforces hook rules at compile time
Watch Out!
The most common hook rule violation: calling useEffect inside a conditional. If you need different effects for different conditions, use one useEffect with an if inside the body — not multiple conditional useEffects.
Remember
Rule 1: Hooks at top level — never inside if/for/nested functions. Rule 2: Hooks only in React function components or custom hooks. Install eslint-plugin-react-hooks and it will catch violations automatically.
What You Learned
- •Two rules: call hooks only at top level (not inside conditions/loops); only in React function components or custom hooks
- •React tracks hooks by call order — any conditional call misaligns the slot array causing bugs
- •Unlocks: bug-free hook usage, understanding why hooks work the way they do
Key Facts
- →React 18.2 added a new lint rule for exhaustive-deps — another hook rule for useEffect
- →The rules are enforced by the
eslint-plugin-react-hookspackage (bundled in Create React App and Vite) - →Custom hooks MUST start with 'use' — this is how React identifies them for the linter
- →React Compiler (React 19+) enforces hook rules at compile time
Real-World Examples
Remember
Rule 1: Hooks at top level — never inside if/for/nested functions. Rule 2: Hooks only in React function components or custom hooks. Install eslint-plugin-react-hooks and it will catch violations automatically.
Quick Quiz
Hooks must be called?