This error occurs when the number of React hooks called changes between renders, typically because hooks are placed inside conditions, loops, or after early returns. React requires hooks to be called in the exact same order on every render to properly track component state.
This error indicates that React detected a change in the number of hooks being called during a component's render cycle. React hooks (like useState, useEffect, useContext) must be called in the exact same order on every render because React tracks them using their call order in an internal linked list structure. When a hook is skipped or called conditionally on one render but not the next, React's internal tracking becomes misaligned. The framework expects the same sequence of hooks each time, and any deviation causes this error to be thrown. This is a fundamental requirement of how React's hooks system works - it relies on consistent call order rather than unique identifiers to associate each hook with its corresponding state and effects. The error message specifically means that on the current render, React encountered more hook calls than it did on the previous render, which violates the Rules of Hooks and indicates that hook calls are happening conditionally or in an inconsistent manner.
Ensure all hook calls happen before any conditional logic or return statements. Hooks must always execute in the same order.
Before (incorrect):
function MyComponent({ isLoggedIn }) {
if (!isLoggedIn) {
return <Login />;
}
// ❌ This hook only runs when isLoggedIn is true
const [user, setUser] = useState(null);
return <div>{user?.name}</div>;
}After (correct):
function MyComponent({ isLoggedIn }) {
// ✅ Hook is always called at the top level
const [user, setUser] = useState(null);
if (!isLoggedIn) {
return <Login />;
}
return <div>{user?.name}</div>;
}If you need conditional behavior, place the condition inside the hook rather than conditionally calling the hook itself.
Before (incorrect):
function MyComponent({ shouldFetch }) {
if (shouldFetch) {
// ❌ Hook only called when shouldFetch is true
useEffect(() => {
fetchData();
}, []);
}
}After (correct):
function MyComponent({ shouldFetch }) {
// ✅ Hook always called, condition moved inside
useEffect(() => {
if (shouldFetch) {
fetchData();
}
}, [shouldFetch]);
}When you need to conditionally use hooks, create a separate component that always calls its hooks in a consistent order.
Before (incorrect):
function Dashboard({ userType }) {
if (userType === 'admin') {
const [stats, setStats] = useState(null); // ❌ Conditional hook
return <AdminDashboard stats={stats} />;
}
return <UserDashboard />;
}After (correct):
function AdminDashboardWrapper() {
const [stats, setStats] = useState(null); // ✅ Always called
return <AdminDashboard stats={stats} />;
}
function Dashboard({ userType }) {
if (userType === 'admin') {
return <AdminDashboardWrapper />;
}
return <UserDashboard />;
}If you're calling component functions directly instead of using JSX, this can disrupt React's render cycle and hook tracking.
Before (incorrect):
function App() {
const [count, setCount] = useState(0);
// ❌ Calling component as a function
return MyComponent({ count });
}After (correct):
function App() {
const [count, setCount] = useState(0);
// ✅ Using JSX syntax
return <MyComponent count={count} />;
}Never call hooks while iterating over arrays or in any kind of loop structure.
Before (incorrect):
function ItemList({ items }) {
return items.map(item => {
// ❌ Hook called in a loop - number changes with items.length
const [selected, setSelected] = useState(false);
return <Item key={item.id} selected={selected} />;
});
}After (correct):
function ListItem({ item }) {
// ✅ Hook in separate component
const [selected, setSelected] = useState(false);
return <Item selected={selected} />;
}
function ItemList({ items }) {
return items.map(item => (
<ListItem key={item.id} item={item} />
));
}Install and configure eslint-plugin-react-hooks to automatically detect hook rule violations during development.
npm install eslint-plugin-react-hooks --save-devAdd to your .eslintrc.json:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}This will show errors in your editor when you violate the Rules of Hooks, catching issues before runtime.
If you're using custom hooks, ensure they also follow the Rules of Hooks and don't call hooks conditionally.
Before (incorrect):
function useAuth(requireAuth) {
if (requireAuth) {
// ❌ Conditional hook in custom hook
const user = useContext(AuthContext);
return user;
}
return null;
}After (correct):
function useAuth() {
// ✅ Always call the hook
const user = useContext(AuthContext);
return user;
}
// Use conditional logic at the call site instead
function MyComponent({ requireAuth }) {
const user = useAuth();
if (requireAuth && !user) {
return <Login />;
}
return <Dashboard />;
}Why React uses call order: React's hooks implementation relies on a linked list data structure where each hook call adds a node in sequence. This design choice makes the implementation fast and memory-efficient, but requires strict ordering rules. Alternative designs using unique IDs or keys were considered but rejected due to performance overhead and developer experience concerns.
Next.js and Server Components: In Next.js 13+, be aware that Server Components cannot use hooks at all. Only Client Components (marked with 'use client') can use hooks. Mixing these patterns incorrectly can cause confusing hook errors.
Testing considerations: When writing tests, ensure your test cases exercise all code paths. A component might work in one test scenario but fail in another if hooks are conditionally called based on props or state that differ between tests.
React DevTools: The React DevTools extension can help visualize the hooks tree for a component. When debugging this error, use DevTools to inspect which hooks are being called and in what order during different renders.
Concurrent React and Suspense: With React 18's concurrent features, components may render multiple times or partially. Hook order violations become even more critical in this context as renders may be interrupted and restarted.
Prevention best practices:
- Always use the ESLint plugin for real-time feedback
- Extract complex conditional rendering into separate components
- Keep all hooks at the very top of your function body
- Use TypeScript to catch structural issues earlier
- Review custom hooks with the same scrutiny as components
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React