<StrictMode> es un componente de React que activa comprobaciones extra para todo lo que contiene, solo en desarrollo. Renderiza cada componente dos veces, ejecuta la configuración, la limpieza y otra vez la configuración de cada efecto al montar, ejecuta las ref callbacks dos veces y avisa sobre APIs obsoletas. Por eso useEffect "se ejecuta dos veces" en desarrollo. En un build de producción no hace nada.
Lo activas envolviendo tu app, normalmente en main.jsx:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
);
Las vistas previas de esta página funcionan como un build de producción, así que StrictMode no tendría ningún efecto visible en ellas. En su lugar, cada ejemplo de abajo tiene el tipo de error que StrictMode existe para detectar, y te deja provocarlo a mano. Aquí un efecto agrega un listener de clic en document y nunca lo quita. Oculta y muestra el panel un par de veces, y luego haz clic en la página.
Cada Show agrega otro listener, y ninguno se quita, así que un solo clic registra una vez por cada montaje. Es una fuga que un usuario podría causar abriendo y cerrando un panel. StrictMode la mostraría en el primer montaje: en desarrollo monta, desmonta y vuelve a montar, así que verías dos líneas de registro por clic antes de tocar el botón.
Efectos: configuración, limpieza, configuración
Al montar, StrictMode ejecuta tu efecto, luego su limpieza y luego otra vez el efecto. Está simulando lo que pasa cuando un usuario sale de una pantalla y vuelve, y comprueba que tu limpieza deshace tu configuración. Si lo hace, la segunda configuración deja las cosas exactamente como las dejaría una sola, y no ves nada raro. Si no lo hace, el error aparece de inmediato.
La solución para el logger es una limpieza que quite la misma función que agregó:
Alterna tantas veces como quieras: cada clic registra una línea mientras se muestra el logger. Con StrictMode en desarrollo la consola empezaría con setup, cleanup, setup, y la app se seguiría comportando bien. Esa secuencia es la señal de un efecto sano, no un problema que haya que desactivar.
Efectos habituales que necesitan una limpieza: event listeners, setInterval y setTimeout, suscripciones, conexiones WebSocket o de chat, y peticiones cuya respuesta debe ignorarse si el componente ya no está. La página de useEffect muestra el patrón de limpieza para cada uno.
Renderizados: llamados dos veces
StrictMode también llama a tu función de componente dos veces en cada renderizado (y a las funciones que pasas a useState, useMemo y useReducer, y a los constructor, render y shouldComponentUpdate de las clases). React conserva el resultado de una llamada y descarta el de la otra. Un componente puro devuelve el mismo JSX las dos veces, así que nada cambia.
Un componente que cambia algo fuera de sí mismo mientras renderiza no es puro, y llamarlo dos veces hace que el cambio ocurra dos veces. Este agrega una entrada "rendered at" a un array que recibe como prop:
Cada nuevo renderizado agrega una entrada que nadie pidió. Aquí solo lo notas después de hacer clic. Con StrictMode, el primer renderizado ya mostraría dos entradas nuevas en lugar de una. La solución es calcular durante el renderizado sin mutar: const shown = [...entries, 'extra'].
React 19 agrega un detalle: durante el doble renderizado, useMemo y useCallback reutilizan el resultado de la primera llamada en lugar de calcularlo dos veces.
Ref callbacks
Desde React 19, StrictMode también ejecuta las ref callbacks dos veces al montar: conectar, limpiar, conectar. Una ref callback que agrega un listener o registra el nodo en algún lugar necesita devolver una limpieza, exactamente como un efecto:
<div
ref={(node) => {
const observer = new ResizeObserver(onResize);
observer.observe(node);
return () => observer.disconnect();
}}
/>
Avisos de APIs obsoletas
StrictMode avisa sobre APIs que están de salida, sobre todo en componentes de clase, como los viejos métodos de ciclo de vida UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps y UNSAFE_componentWillUpdate. Algunas APIs sobre las que avisaba, como las string refs y findDOMNode, se eliminaron por completo de React 19.
Activarlo para una parte de la app
<StrictMode> es un componente normal, así que puedes envolver solo una parte del árbol. Esto ayuda al adoptarlo en una app grande: empieza con las pantallas nuevas y ve ampliando.
function App() {
return (
<>
<Header />
<StrictMode>
<Sidebar />
<Content />
</StrictMode>
<Footer />
</>
);
}
Sidebar y Content (y todo lo que renderizan) reciben las comprobaciones; Header y Footer no. No hay forma de desactivarlo para un subárbol dentro de una parte estricta.
Dónde viene activado por defecto:
- Vite: la plantilla de React envuelve
<App />en<StrictMode>enmain.jsx. - Next.js: el App Router lo activa por defecto;
reactStrictModeennext.config.jslo controla.
No hace nada en producción
Un build de producción ignora <StrictMode>: un renderizado, una ejecución de cada efecto, ningún aviso. Así que las llamadas dobles nunca le cuestan nada a tus usuarios, y no hay razón para quitar el envoltorio antes de publicar.
Si un efecto doble rompe algo en desarrollo, como una petición enviada dos veces o un evento de analítica registrado dos veces, eso es información. La mayoría de las veces la solución es una limpieza (una bandera ignore para una petición, una desconexión para una conexión). Para algo que de verdad debe ocurrir una vez por carga de la app, como inicializar un SDK, ejecútalo fuera de cualquier componente a nivel de módulo, o protégelo con una bandera a nivel de módulo.
let didInit = false;
function App() {
useEffect(() => {
if (didInit) return;
didInit = true;
initAnalytics(); // runs once per page load, even in StrictMode
}, []);
}
Evita el atajo habitual de una ref que se salta la segunda ejecución del efecto. Oculta el aviso sin corregir la limpieza que falta, y el error vuelve la primera vez que el componente se desmonta y se vuelve a montar de verdad, como hace el logger de arriba.
Preguntas frecuentes
¿Qué hace StrictMode en React?
Solo en desarrollo, renderiza dos veces cada componente que contiene, ejecuta la configuración, la limpieza y otra vez la configuración de cada efecto cuando el componente se monta, ejecuta las ref callbacks dos veces de la misma forma y avisa sobre APIs obsoletas. Las llamadas extra exponen los renderizados que no son puros y los efectos a los que les falta la limpieza.
¿Por qué mi useEffect se ejecuta dos veces?
Porque el componente está dentro de <StrictMode> y estás usando un build de desarrollo. React lo monta, simula un desmontaje (se ejecuta tu limpieza) y lo vuelve a montar. Si la segunda configuración causa un error, al efecto le falta una limpieza.
¿StrictMode afecta a producción?
No. Un build de producción renderiza una vez y ejecuta cada efecto una vez, con o sin <StrictMode>. Puedes dejarlo en tu código.
¿Cómo desactivo StrictMode?
Quita el envoltorio <StrictMode>, normalmente en main.jsx o index.js, o pon reactStrictMode: false en next.config.js. Corregir la limpieza suele ser mejor: la doble ejecución señala un error con el que se pueden topar los usuarios reales.
¿Por qué console.log se imprime dos veces?
StrictMode llama a tu función de componente dos veces por renderizado en desarrollo. React DevTools puede mostrar en gris los registros de la segunda llamada, u ocultarlos, desde su configuración.