'Invalid hook call' in React: every cause and how to fix each one
Hooks can only be called inside a function component - but you knew that and you're still getting the error. Here's the full list of causes, including the duplicate-React problem nobody tells you about.
'Invalid hook call. Hooks can only be called inside of the body of a function component.' The error message lists three possible causes, and infuriatingly, the one that bites most people in real projects - two copies of React in the bundle - is the one that has nothing to do with your code.
Cause 1: actually breaking the Rules of Hooks
// โ Conditional hook
if (user) { const [tab, setTab] = useState('home'); }
// โ Hook after an early return
if (!data) return <Spinner />;
const [open, setOpen] = useState(false);
// โ
Hooks first, branching after
const [open, setOpen] = useState(false);
if (!data) return <Spinner />;React tracks hooks by call order, so every render must call the same hooks in the same sequence. No hooks in conditions, loops, nested functions, event handlers, or after early returns. Also check you're not calling a hook from a regular function - only components (capitalized, returning JSX) and custom hooks (named useSomething) may call hooks.
Cause 2: two copies of React
If your code is clean but the error persists - especially right after installing a component library or linking a local package - you almost certainly have duplicate React instances. Hooks rely on shared internal state between react and react-dom; a second copy of react breaks that contract.
# Diagnose
npm ls react react-dom
# Fix: force one copy
npm dedupe
# For npm link / monorepo setups, alias in vite.config.ts:
# resolve: { alias: { react: path.resolve('./node_modules/react') } }The classic trigger is `npm link`-ing a library that has react in dependencies instead of peerDependencies - the linked package brings its own React. Libraries must declare React as a peer dependency; if you maintain one, that's your fix.
Cause 3: mismatched react and react-dom versions
react@19 with react-dom@18 (or vice versa) produces the same error. Pin both to the same version and reinstall. In monorepos, hoisting can silently give different workspaces different versions - `npm ls` from the app that crashes, not the repo root.
Quick triage order
- Did it start after an npm install or npm link? โ duplicate React. Run npm ls react.
- Does the stack trace point at your component? โ Rules of Hooks violation. Check for conditions and early returns above the hook.
- Is it a class component? Hooks don't work there at all - convert to a function component.
- Only happens in production build? Check that your bundler isn't inlining a second React from a pre-bundled dependency.
Written by Appesto Engineering.