Back to React
React
medium
mid

What is React Strict Mode and how does it affect development?

Strict Mode is a dev-only React wrapper that surfaces unsafe patterns: double-invokes components/effects/state initializers, warns on deprecated APIs (legacy refs, findDOMNode), forbids side-effects in render. The double-mount in React 18 specifically reveals missing useEffect cleanups. Production is unchanged. Always wrap the app in <StrictMode> during development.

3 min read·~6 min to think through

Strict Mode is a development-only React wrapper that helps surface bugs that would otherwise lurk until they bite in production.

Setup

tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

What it does

  1. Double-invokes components, effects, state initializers, and reducers in dev.
  2. Warns on legacy APIs: findDOMNode, string refs, legacy context, deprecated lifecycles (UNSAFE_componentWillMount, etc.).
  3. Detects side-effects in render (warnings in dev when something would break concurrent rendering).
  4. Forces you to handle effect cleanup correctly — the double-mount surfaces leaks.

The double-mount in React 18

tsx
useEffect(() => {
  console.log('mount');
  return () => console.log('cleanup');
}, []);

In dev with StrictMode, this logs: mountcleanupmount. React simulates an unmount + remount to make sure your effect handles being re-run safely.

If you see a bug only with StrictMode (subscribe twice, fetch twice), the fix is not to disable StrictMode — it's to add proper cleanup so the second run is idempotent.

Why this matters

React 18's concurrent rendering can interrupt and re-run renders. Effects that aren't properly cleaned up will leak in production under realistic load. StrictMode surfaces the bug in dev where it's cheap to fix.

Common surfaces

IssueSymptomFix
Effect with no cleanupAPI hit twiceAbortController in cleanup
Subscription leaksListener fires twiceUnsubscribe in cleanup
Non-idempotent state initState mutatesUse updater fn in setState
Side-effects in renderInconsistent UIMove to useEffect
Legacy lifecycle methodsWarningMigrate to hooks

Production

StrictMode is completely no-op in production. No double-mount, no extra cost, no warnings. So you can leave it on permanently.

Common confusion

  • "Strict Mode breaks my code" — usually means your effect doesn't clean up properly. The "breakage" is exposing an existing bug.
  • "My counter goes from 0 to 2 in dev" — likely setting state without an updater fn or a setState during render.

Migrating an existing app

Wrap the app, run dev, fix everything it warns about. Often catches 5–10 real bugs in a medium app.

Interview framing

"Dev-only wrapper that surfaces unsafe patterns. Double-invokes components, effects, state initializers; warns on legacy APIs; detects side-effects in render. The double-mount specifically reveals missing useEffect cleanups — if your effect breaks under StrictMode, it would leak in production under concurrent rendering or remounts. The right fix is always to add cleanup, never to disable StrictMode. Production behavior is unchanged."

Follow-up questions

  • Why does StrictMode double-mount?
  • When have you fixed a real bug exposed by StrictMode?
  • What does it warn about that hooks don't catch?

Common mistakes

  • Disabling StrictMode to silence warnings.
  • Setting state in render.
  • Effects without cleanup.

Performance considerations

  • No prod cost. Dev double-invokes slow down dev cycles slightly; worth the bug-catching.

Edge cases

  • Storybook + StrictMode + fast-refresh interactions.
  • Legacy lifecycle methods in nested vendor components.
  • Initial-mount setup that's expensive (intentional one-time work).

Real-world examples

  • React 18 upgrade guides, double-fetch debugging stories.

Senior engineer discussion

Seniors leave StrictMode on permanently and fix what it surfaces; they recognize 'breaks in StrictMode' as a real bug.

Related questions