React
StrictMode: why effects run twice
In development StrictMode mounts, unmounts and remounts each component to test cleanup: if the effect runs twice, a cleanup is missing.
Key points
- StrictMode doubles mounting in development only, to surface a missing cleanup.
- In production the mount effect runs once: the double call is not a bug.
- An effect that fetches needs an AbortController, or the response arrives twice.
StrictMode doubles effect mounting in development only, to force every effect to unmount cleanly: when the second pass breaks something, a cleanup is missing.
What it does exactly
React 19 mounts the component, runs the effect, unmounts it by running the cleanup function and mounts it again. No line of your code changed: the second pass is a test, not a change in expected behaviour. In production the cycle happens once.
The effect that survives the test
useEffect(() => {
const controller = new AbortController();
fetch("/api/projects", { signal: controller.signal })
.then((response) => response.json())
.then(setProjects)
.catch((error) => {
if (error.name !== "AbortError") console.error(error);
});
return () => controller.abort();
}, []);
On the first unmount abort() cancels the request in flight; the second mount starts a new one. Without cleanup the first response would write to the state of a component that is already gone.
The usual symptoms
- Two requests: a doubled
POSTin development is the most common sign of an unguarded effect. - Duplicate listeners:
addEventListenerwithoutremoveEventListenerstacks handlers on every mount. - Double timers: a
setIntervalwithoutclearIntervaldoubles its rate after the remount.
A project to try it
npm run build
npm run preview
The production build contains no double mount: if the problem disappears with npm run preview and stays with npm run dev, you have found a missing cleanup.
Live records in the catalogue
Three components that read a preference in a mount effect: theme-toggle, auth-switch and sky-toggle. To browse by category start from 111,985 components.
The limitation
StrictMode checks that a cleanup exists, not that the effect is correct: an effect with the wrong dependencies stays wrong under a double mount, and no warning flags it. It does not replace tests, and in development it can double calls to external services that cannot be cancelled.
Where to continue
What changes in React 19 is in React 19; the boundary between server components and hooks is in server components and hooks; the ecosystem comparison is in Vue 3 vs React.
Frequently asked questions
Is the double effect a React bug?
No, it is deliberate and development only. It surfaces effects without cleanup, which would cause double requests in production.
Can I turn StrictMode off?
Yes, by removing the wrapper, but you lose the cleanup check. Fixing the effects is better: production does not change.
Why does the request fire twice in development?
Because the first mount is unmounted straight away and the request is not cancelled. Add an AbortController and the first pass stops.
Does StrictMode double renders too?
Yes, in development it runs the component body and reducers twice, to make side effects during render visible.
Does StrictMode apply to Server Components?
No. The double mount concerns the client; Server Components run on the server and have no effects.