Few React errors stop work as completely as this one: hooks can only be called inside the body of a function component. The page goes blank, the console fills with warnings about invalid hook calls, and the code often looks perfectly normal at first glance.
The good news is that this error has a small set of well-known causes. This guide walks through each one, from calling a hook in the wrong place to the sneaky problem of having two copies of React in the same project, so you can find your cause quickly and fix it for good.
Quick Answer
This error means a hook such as useState or useEffect was called outside a React function component or custom hook, or React could not recognise the call. Fix it by calling hooks only at the top level of a function component or custom hook, never inside loops, conditions, nested functions, or class components, and by making sure your project uses a single copy of React.
What the Error Really Means
Hooks are special functions that let a function component use React features such as state and effects. React tracks hooks by the order in which they are called during a component’s render. For that tracking to work, React must know which component is currently rendering when a hook runs.
When you call useState in a place React does not associate with a component render, React has nowhere to store the state. It throws the invalid hook call error, which appears in several forms. The message about hooks only being called inside the body of a function component is one of them. Another common version complains about a mismatch between the number of hooks in successive renders.
So the error is not really about the hook itself. It is about the context in which the hook was called.
The Rules You Must Follow
React’s rules of hooks are simple, and most cases of this error are a broken rule:
- Only call hooks at the top level. Never call a hook inside a loop, a condition, a nested function, a try block, or after an early return.
- Only call hooks from React functions. That means function components and custom hooks, which are functions whose names start with
use. Never call hooks from regular JavaScript functions, event handlers, class components, or plain helper files.
The official React documentation on the rules of hooks explains why call order matters and shows correct and incorrect patterns side by side.
How to Fix It Step by Step
Step 1: Find Hooks Called Conditionally or in Loops
Search the failing component for hooks placed after an if, inside a condition, or inside a loop. This pattern is broken:
if (isLoggedIn) { const [name, setName] = useState(""); }
It fails because on renders where the condition is false, the hook disappears, and React’s hook order breaks. Move the hook to the top level and use the value conditionally instead: call useState always, then decide in your JSX or logic whether to use the result.
The same rule applies to early returns. If your component returns early when data is loading, place that return after all hook calls, never before them.
Step 2: Move Hooks Out of Event Handlers and Nested Functions
A frequent beginner mistake is calling a hook inside a click handler or a helper defined inside the component:
function handleClick() { const navigate = useNavigate(); }
Handlers run in response to events, not during render, so React is not tracking them. Call the hook at the top of the component, store the result in a variable, and use that variable inside the handler.
Step 3: Check You Are Not Calling Hooks in Class Components
Hooks do not work in class components at all. If the failing code lives in a class, you have two choices: rewrite that component as a function component, or keep the class and use the older patterns it supports, such as state fields and lifecycle methods, or a higher-order component or render prop that bridges to a hook-using function component.
Step 4: Check for Hooks in Regular Utility Functions
If a shared helper function needs hook behaviour, turn it into a custom hook. Rename it so it starts with use, such as useWindowWidth, and call it from components like any other hook. A plain function named getWindowWidth cannot call useState, no matter where the file lives.
Step 5: Rule Out Multiple Copies of React
If every hook looks correctly placed and the error persists, suspect a duplicate React. This happens in monorepos, when a library bundles its own React, or when a package is linked locally. React and the renderer must share one copy, otherwise the hook system and the component tree belong to different React instances and cannot see each other.
- Check your package manager output and lockfile for more than one React version.
- Make sure libraries list React as a peer dependency instead of bundling it.
- In bundlers, alias
reactandreact-domto a single path if a linked package resolves its own copy. - Reinstall dependencies cleanly if the tree has become tangled, and verify versions match between
reactandreact-dom.
Step 6: Verify Component Names and JSX Usage
React recognises components by capitalised names used as JSX or passed to React APIs. If you call a function component as a plain function, such as {MyComponent()} instead of <MyComponent />, any hooks inside it run in the caller’s context and break the rules. Always render components as JSX, and keep their names capitalised.
Our broader React troubleshooting guides cover the failures that often appear alongside this one, including the list warning explained in our fix for the React unique key prop warning in lists, which is a useful next check when a page renders but logs errors.
Common Mistakes and Useful Tips
- Calling a hook inside a callback passed to another hook. The callback given to
useEffectoruseMemois not a component body. Hooks inside those callbacks trigger this error. - Importing hooks from the wrong package. Import React hooks from
reactitself. A misspelled or third-party import can bind to a different React copy. - Mixing React versions. Using React 18 with an older
react-dom, or the reverse, can produce hook errors that look like code mistakes. - Testing hooks incorrectly. Calling a custom hook directly in a test, outside a component, fails the same way. Use a small test component or a hook-testing utility instead.
- Ignoring the ESLint plugin. The React Hooks lint rules catch conditional hooks, misplaced hooks, and many ordering problems while you type. Enable them in every project.
A reliable debugging tip is to comment out hooks in the failing component one block at a time. The moment the error changes or disappears, you have narrowed the search, and the cause is usually visible in that block’s placement.
Conclusion
The message about hooks only being called inside a function component always points to context: a hook ran where React could not track it, or React itself was split into two copies. Keep hooks at the top level of function components and custom hooks, never hide them in conditions, loops, handlers, or classes, render components as JSX, and keep a single matching React and ReactDOM version. Follow those checks in order and this intimidating error becomes a quick, routine fix.
Frequently Asked Questions
Why does the error appear after I install a new library?
The library may bundle its own copy of React or require a different React version. Check for duplicate React installations, confirm the library treats React as a peer dependency, and align your React and ReactDOM versions.
Can I call a hook inside a custom hook?
Yes. Custom hooks are designed for exactly that. A function whose name starts with use may call other hooks at its top level, and components then call the custom hook at their top level.
Why did my code work before and break after refactoring?
Refactoring often moves a hook behind a condition, an early return, or into a helper function without anyone noticing. Compare the hook call order before and after the change; the moved hook is usually the culprit.
Does this error happen with server components?
Yes, if you use a client-only hook such as useState in a server component in frameworks that support them. Move stateful logic into a separate client component marked for the client, and keep server components free of hooks that need the browser or component state.
How can I prevent this error in a team project?
Enable the React Hooks ESLint rules, keep dependency versions locked, and review new custom hooks carefully. Most occurrences are caught automatically once the lint rules are treated as errors in your build.


Leave a Reply