useEffect Runs Twice in
React Strict Mode — Why & How to Fix It
React 19 • Next.js 15 • TypeScript • Vite • React Router • Redux Toolkit
🌀 Why Does useEffect Run Twice in Development?
If you've been working with React 18+ in development mode, you've probably noticed
that useEffect hooks run twice on mount. This isn't a bug —
it's a deliberate feature of React Strict Mode designed to help you
identify and fix side-effect bugs before they reach production.
The behavior looks like this:
// Console output in development with Strict Mode:
useEffect ran! // First mount
useEffect cleanup! // Cleanup before re-mount
useEffect ran! // Second mount (re-run)
Why does React do this? React Strict Mode intentionally mounts, unmounts, and re-mounts components in development to:
- Detect side-effect bugs — If your effect doesn't clean up properly, you'll notice it when the effect runs twice.
- Prepare for concurrent features — React 18+ can mount, unmount, and re-mount components during rendering (suspense, transitions).
- Enforce pure components — Your components should work correctly even if they mount and unmount multiple times.
- Catch memory leaks — If you don't clean up event listeners, subscriptions, or timers, you'll see the problem quickly.
⚡ Quick Reference — Key Facts at a Glance
📋 Golden Rules for useEffect in Strict Mode
-
Always return a cleanup function — Every
useEffectthat creates side effects should clean them up. - Don't rely on mount/unmount order — Your effect should work correctly even if it runs multiple times.
-
Use
useReffor one-time initialization — Track if the effect has already run with a ref. - Include all dependencies — Missing dependencies can cause bugs that double execution reveals.
- Embrace Strict Mode — It helps you write more robust, production-ready code.
-
Use
useEffectfor side effects only — Not for data fetching that should only happen once (use libraries like SWR, React Query, orusehook).
🧩 Detailed Breakdown — Every Scenario Solved
Each card below dives deep into a specific aspect of useEffect double
execution, with root causes, code examples, and step-by-step fixes.
🛡️ Prevention — Write Robust useEffect Hooks
Every
useEffect that creates subscriptions, timers, or event listeners
should return a cleanup function.
useRef for One-Time InitializationTrack if your effect has already run using
useRef(false) to prevent
double execution in development.
useEffect for Side Effects OnlyFor data fetching, consider using libraries like SWR, React Query, or the new
use hook in React 19.
The React
exhaustive-deps ESLint rule helps you catch missing dependencies.
Remember that double execution only happens in development. Build and test your app in production mode to verify behavior.
use hook for promises
works differently from useEffect and may not have the same double-execution
behavior in Strict Mode. Consider using use for data fetching.
❓ Frequently Asked Questions
useEffect run twice in production?
No. useEffect only runs twice in
development mode when React Strict Mode is enabled.
In production builds, it runs once (or whenever dependencies change).
This is because Strict Mode is a development-only tool that helps catch side-effect bugs. It's automatically disabled in production builds.
No. Disabling Strict Mode to "fix" double execution is a bad practice. Strict Mode helps you catch bugs early and write more robust code.
Instead of disabling it, learn to write useEffect hooks
that work correctly even when run twice. The double execution is a
feature, not a bug.
No. React 19 maintains the same Strict Mode behavior
as React 18. useEffect still runs twice in development
with Strict Mode enabled.
However, React 19 introduces the use hook for promises
and contexts, which may be a better choice for data fetching in some cases.
The use hook has different behavior and may not double-execute
in the same way.
Best practices for debugging:
- Add console logs — Log when the effect runs and cleans up.
- Check your cleanup function — Make sure it properly cleans up all side effects.
- Use
useRef— Track if the effect has already run. - Check dependencies — Missing dependencies can cause unexpected behavior.
- Build in production mode — Verify that the double execution doesn't happen in production.
- Use React DevTools — Inspect component mounts and unmounts.
useLayoutEffect? Does it run twice too?
Yes. useLayoutEffect also runs twice in
Strict Mode development, just like useEffect.
The same rules apply: always return a cleanup function and ensure your layout effects work correctly even when run multiple times.
// ✅ Both useEffect and useLayoutEffect need cleanup
useLayoutEffect(() => {
const observer = new ResizeObserver(() => {});
observer.observe(element);
return () => {
observer.disconnect(); // ✅ Cleanup
};
}, []);
No. Server components in Next.js 15 don't use hooks
like useEffect. They're rendered on the server and don't
have the same lifecycle.
However, if you're using client components with 'use client',
the double execution behavior applies in development with Strict Mode.
Client components still follow the same React rules.
// ✅ Client component with useEffect
'use client';
function MyComponent() {
useEffect(() => {
// This runs twice in development with Strict Mode
console.log('Effect ran!');
return () => {
console.log('Cleanup!');
};
}, []);
return <div>Hello</div>;
}
use
hook for data fetching in server components, which behaves differently
from useEffect.

0 Comments
thanks for your comments!