<StrictMode> lets you find common bugs in your components early during development.
<StrictMode>
<App/>
</StrictMode>Reference
<StrictMode>
UseStrictMode to enable additional development behaviors and warnings for the component tree inside:
import{StrictMode}from'react';
import{createRoot}from'react-dom/client';
constroot =createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App/>
</StrictMode>
);Strict Mode enables the following development-only behaviors:
- Your components willre-render an extra time to find bugs caused by impure rendering.
- Your components willre-run Effects an extra time to find bugs caused by missing Effect cleanup.
- Your components willre-run refs callbacks an extra time to find bugs caused by missing ref cleanup.
- Your components willbe checked for usage of deprecated APIs.
Props
StrictMode accepts no props.
Caveats
- There is no way to opt out of Strict Mode inside a tree wrapped in
<StrictMode>. This gives you confidence that all components inside<StrictMode>are checked. If two teams working on a product disagree whether they find the checks valuable, they need to either reach consensus or move<StrictMode>down in the tree.
Usage
Enabling Strict Mode for entire app
Strict Mode enables extra development-only checks for the entire component tree inside the<StrictMode> component. These checks help you find common bugs in your components early in the development process.
To enable Strict Mode for your entire app, wrap your root component with<StrictMode> when you render it:
import{StrictMode}from'react';
import{createRoot}from'react-dom/client';
constroot =createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App/>
</StrictMode>
);We recommend wrapping your entire app in Strict Mode, especially for newly created apps. If you use a framework that callscreateRoot for you, check its documentation for how to enable Strict Mode.
Although the Strict Mode checksonly run in development, they help you find bugs that already exist in your code but can be tricky to reliably reproduce in production. Strict Mode lets you fix bugs before your users report them.
Note
Strict Mode enables the following checks in development:
- Your components willre-render an extra time to find bugs caused by impure rendering.
- Your components willre-run Effects an extra time to find bugs caused by missing Effect cleanup.
- Your components willre-run ref callbacks an extra time to find bugs caused by missing ref cleanup.
- Your components willbe checked for usage of deprecated APIs.
All of these checks are development-only and do not impact the production build.
Enabling Strict Mode for a part of the app
You can also enable Strict Mode for any part of your application:
import{StrictMode}from'react';
functionApp(){
return(
<>
<Header/>
<StrictMode>
<main>
<Sidebar/>
<Content/>
</main>
</StrictMode>
<Footer/>
</>
);
}In this example, Strict Mode checks will not run against theHeader andFooter components. However, they will run onSidebar andContent, as well as all of the components inside them, no matter how deep.
Note
WhenStrictMode is enabled for a part of the app, React will only enable behaviors that are possible in production. For example, if<StrictMode> is not enabled at the root of the app, it will notre-run Effects an extra time on initial mount, since this would cause child effects to double fire without the parent effects, which cannot happen in production.
Fixing bugs found by double rendering in development
React assumes that every component you write is a pure function. This means that React components you write must always return the same JSX given the same inputs (props, state, and context).
Components breaking this rule behave unpredictably and cause bugs. To help you find accidentally impure code, Strict Mode calls some of your functions (only the ones that should be pure)twice in development. This includes:
- Your component function body (only top-level logic, so this doesn’t include code inside event handlers)
- Functions that you pass to
useState,setfunctions,useMemo, oruseReducer - Some class component methods like
constructor,render,shouldComponentUpdate(see the whole list)
If a function is pure, running it twice does not change its behavior because a pure function produces the same result every time. However, if a function is impure (for example, it mutates the data it receives), running it twice tends to be noticeable (that’s what makes it impure!) This helps you spot and fix the bug early.
Here is an example to illustrate how double rendering in Strict Mode helps you find bugs early.
ThisStoryTray component takes an array ofstories and adds one last “Create Story” item at the end:
exportdefaultfunctionStoryTray({stories}){constitems =stories;items.push({id:'create',label:'Create Story'});return(<ul>{items.map(story=>(<likey={story.id}>{story.label}</li>))}</ul>);}
There is a mistake in the code above. However, it is easy to miss because the initial output appears correct.
This mistake will become more noticeable if theStoryTray component re-renders multiple times. For example, let’s make theStoryTray re-render with a different background color whenever you hover over it:
import{useState}from'react';exportdefaultfunctionStoryTray({stories}){const[isHover,setIsHover] =useState(false);constitems =stories;items.push({id:'create',label:'Create Story'});return(<ulonPointerEnter={()=>setIsHover(true)}onPointerLeave={()=>setIsHover(false)}style={{backgroundColor:isHover ?'#ddd' :'#fff'}}>{items.map(story=>(<likey={story.id}>{story.label}</li>))}</ul>);}
Notice how every time you hover over theStoryTray component, “Create Story” gets added to the list again. The intention of the code was to add it once at the end. ButStoryTray directly modifies thestories array from the props. Every timeStoryTray renders, it adds “Create Story” again at the end of the same array. In other words,StoryTray is not a pure function—running it multiple times produces different results.
To fix this problem, you can make a copy of the array, and modify that copy instead of the original one:
exportdefaultfunctionStoryTray({stories}){
constitems =stories.slice();// Clone the array
// ✅ Good: Pushing into a new array
items.push({id:'create',label:'Create Story'});This wouldmake theStoryTray function pure. Each time it is called, it would only modify a new copy of the array, and would not affect any external objects or variables. This solves the bug, but you had to make the component re-render more often before it became obvious that something is wrong with its behavior.
In the original example, the bug wasn’t obvious. Now let’s wrap the original (buggy) code in<StrictMode>:
exportdefaultfunctionStoryTray({stories}){constitems =stories;items.push({id:'create',label:'Create Story'});return(<ul>{items.map(story=>(<likey={story.id}>{story.label}</li>))}</ul>);}
Strict Modealways calls your rendering function twice, so you can see the mistake right away (“Create Story” appears twice). This lets you notice such mistakes early in the process. When you fix your component to render in Strict Mode, youalso fix many possible future production bugs like the hover functionality from before:
import{useState}from'react';exportdefaultfunctionStoryTray({stories}){const[isHover,setIsHover] =useState(false);constitems =stories.slice();// Clone the arrayitems.push({id:'create',label:'Create Story'});return(<ulonPointerEnter={()=>setIsHover(true)}onPointerLeave={()=>setIsHover(false)}style={{backgroundColor:isHover ?'#ddd' :'#fff'}}>{items.map(story=>(<likey={story.id}>{story.label}</li>))}</ul>);}
Without Strict Mode, it was easy to miss the bug until you added more re-renders. Strict Mode made the same bug appear right away. Strict Mode helps you find bugs before you push them to your team and to your users.
Read more about keeping components pure.
Note
If you haveReact DevTools installed, anyconsole.log calls during the second render call will appear slightly dimmed. React DevTools also offers a setting (off by default) to suppress them completely.
Fixing bugs found by re-running Effects in development
Strict Mode can also help find bugs inEffects.
Every Effect has some setup code and may have some cleanup code. Normally, React calls setup when the componentmounts (is added to the screen) and calls cleanup when the componentunmounts (is removed from the screen). React then calls cleanup and setup again if its dependencies changed since the last render.
When Strict Mode is on, React will also runone extra setup+cleanup cycle in development for every Effect. This may feel surprising, but it helps reveal subtle bugs that are hard to catch manually.
Here is an example to illustrate how re-running Effects in Strict Mode helps you find bugs early.
Consider this example that connects a component to a chat:
import{createRoot}from'react-dom/client';import'./styles.css';importAppfrom'./App';constroot =createRoot(document.getElementById("root"));root.render(<App/>);
There is an issue with this code, but it might not be immediately clear.
To make the issue more obvious, let’s implement a feature. In the example below,roomId is not hardcoded. Instead, the user can select theroomId that they want to connect to from a dropdown. Click “Open chat” and then select different chat rooms one by one. Keep track of the number of active connections in the console:
import{createRoot}from'react-dom/client';import'./styles.css';importAppfrom'./App';constroot =createRoot(document.getElementById("root"));root.render(<App/>);
You’ll notice that the number of open connections always keeps growing. In a real app, this would cause performance and network problems. The issue is thatyour Effect is missing a cleanup function:
useEffect(()=>{
constconnection =createConnection(serverUrl,roomId);
connection.connect();
return()=>connection.disconnect();
},[roomId]);Now that your Effect “cleans up” after itself and destroys the outdated connections, the leak is solved. However, notice that the problem did not become visible until you’ve added more features (the select box).
In the original example, the bug wasn’t obvious. Now let’s wrap the original (buggy) code in<StrictMode>:
import{StrictMode}from'react';import{createRoot}from'react-dom/client';import'./styles.css';importAppfrom'./App';constroot =createRoot(document.getElementById("root"));root.render(<StrictMode><App/></StrictMode>);
With Strict Mode, you immediately see that there is a problem (the number of active connections jumps to 2). Strict Mode runs an extra setup+cleanup cycle for every Effect. This Effect has no cleanup logic, so it creates an extra connection but doesn’t destroy it. This is a hint that you’re missing a cleanup function.
Strict Mode lets you notice such mistakes early in the process. When you fix your Effect by adding a cleanup function in Strict Mode, youalso fix many possible future production bugs like the select box from before:
import{StrictMode}from'react';import{createRoot}from'react-dom/client';import'./styles.css';importAppfrom'./App';constroot =createRoot(document.getElementById("root"));root.render(<StrictMode><App/></StrictMode>);
Notice how the active connection count in the console doesn’t keep growing anymore.
Without Strict Mode, it was easy to miss that your Effect needed cleanup. By runningsetup → cleanup → setup instead ofsetup for your Effect in development, Strict Mode made the missing cleanup logic more noticeable.
Read more about implementing Effect cleanup.
Fixing bugs found by re-running ref callbacks in development
Strict Mode can also help find bugs incallbacks refs.
Every callbackref has some setup code and may have some cleanup code. Normally, React calls setup when the element iscreated (is added to the DOM) and calls cleanup when the element isremoved (is removed from the DOM).
When Strict Mode is on, React will also runone extra setup+cleanup cycle in development for every callbackref. This may feel surprising, but it helps reveal subtle bugs that are hard to catch manually.
Consider this example, which allows you to select an animal and then scroll to one of them. Notice when you switch from “Cats” to “Dogs”, the console logs show that the number of animals in the list keeps growing, and the “Scroll to” buttons stop working:
import{useRef,useState}from"react";exportdefaultfunctionCatFriends(){constitemsRef =useRef([]);const[catList,setCatList] =useState(setupCatList);const[cat,setCat] =useState('neo');functionscrollToCat(index){constlist =itemsRef.current;const{node} =list[index];node.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center",});}constcats =catList.filter(c=>c.type ===cat)return(<><nav><buttononClick={()=>setCat('neo')}>Neo</button><buttononClick={()=>setCat('millie')}>Millie</button></nav><hr/><nav><span>Scroll to:</span>{cats.map((cat,index)=>(<buttonkey={cat.src}onClick={()=>scrollToCat(index)}>{index}</button>))}</nav><div><ul>{cats.map((cat)=>(<likey={cat.src}ref={(node)=>{constlist =itemsRef.current;constitem ={cat:cat,node};list.push(item);console.log(`✅ Adding cat to the map. Total cats:${list.length}`);if(list.length >10){console.log('❌ Too many cats in the list!');}return()=>{// 🚩 No cleanup, this is a bug!}}}><imgsrc={cat.src}/></li>))}</ul></div></>);}functionsetupCatList(){constcatList =[];for(leti =0;i <10;i++){catList.push({type:'neo',src:"https://placecats.com/neo/320/240?" +i});}for(leti =0;i <10;i++){catList.push({type:'millie',src:"https://placecats.com/millie/320/240?" +i});}returncatList;}
This is a production bug! Since the ref callback doesn’t remove animals from the list in the cleanup, the list of animals keeps growing. This is a memory leak that can cause performance problems in a real app, and breaks the behavior of the app.
The issue is the ref callback doesn’t cleanup after itself:
<li
ref={node=>{
constlist =itemsRef.current;
constitem ={animal,node};
list.push(item);
return()=>{
// 🚩 No cleanup, this is a bug!
}
}}
</li>Now let’s wrap the original (buggy) code in<StrictMode>:
import{useRef,useState}from"react";exportdefaultfunctionCatFriends(){constitemsRef =useRef([]);const[catList,setCatList] =useState(setupCatList);const[cat,setCat] =useState('neo');functionscrollToCat(index){constlist =itemsRef.current;const{node} =list[index];node.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center",});}constcats =catList.filter(c=>c.type ===cat)return(<><nav><buttononClick={()=>setCat('neo')}>Neo</button><buttononClick={()=>setCat('millie')}>Millie</button></nav><hr/><nav><span>Scroll to:</span>{cats.map((cat,index)=>(<buttonkey={cat.src}onClick={()=>scrollToCat(index)}>{index}</button>))}</nav><div><ul>{cats.map((cat)=>(<likey={cat.src}ref={(node)=>{constlist =itemsRef.current;constitem ={cat:cat,node};list.push(item);console.log(`✅ Adding cat to the map. Total cats:${list.length}`);if(list.length >10){console.log('❌ Too many cats in the list!');}return()=>{// 🚩 No cleanup, this is a bug!}}}><imgsrc={cat.src}/></li>))}</ul></div></>);}functionsetupCatList(){constcatList =[];for(leti =0;i <10;i++){catList.push({type:'neo',src:"https://placecats.com/neo/320/240?" +i});}for(leti =0;i <10;i++){catList.push({type:'millie',src:"https://placecats.com/millie/320/240?" +i});}returncatList;}
With Strict Mode, you immediately see that there is a problem. Strict Mode runs an extra setup+cleanup cycle for every callback ref. This callback ref has no cleanup logic, so it adds refs but doesn’t remove them. This is a hint that you’re missing a cleanup function.
Strict Mode lets you eagerly find mistakes in callback refs. When you fix your callback by adding a cleanup function in Strict Mode, youalso fix many possible future production bugs like the “Scroll to” bug from before:
import{useRef,useState}from"react";exportdefaultfunctionCatFriends(){constitemsRef =useRef([]);const[catList,setCatList] =useState(setupCatList);const[cat,setCat] =useState('neo');functionscrollToCat(index){constlist =itemsRef.current;const{node} =list[index];node.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center",});}constcats =catList.filter(c=>c.type ===cat)return(<><nav><buttononClick={()=>setCat('neo')}>Neo</button><buttononClick={()=>setCat('millie')}>Millie</button></nav><hr/><nav><span>Scroll to:</span>{cats.map((cat,index)=>(<buttonkey={cat.src}onClick={()=>scrollToCat(index)}>{index}</button>))}</nav><div><ul>{cats.map((cat)=>(<likey={cat.src}ref={(node)=>{constlist =itemsRef.current;constitem ={cat:cat,node};list.push(item);console.log(`✅ Adding cat to the map. Total cats:${list.length}`);if(list.length >10){console.log('❌ Too many cats in the list!');}return()=>{list.splice(list.indexOf(item),1);console.log(`❌ Removing cat from the map. Total cats:${itemsRef.current.length}`);}}}><imgsrc={cat.src}/></li>))}</ul></div></>);}functionsetupCatList(){constcatList =[];for(leti =0;i <10;i++){catList.push({type:'neo',src:"https://placecats.com/neo/320/240?" +i});}for(leti =0;i <10;i++){catList.push({type:'millie',src:"https://placecats.com/millie/320/240?" +i});}returncatList;}
Now on inital mount in StrictMode, the ref callbacks are all setup, cleaned up, and setup again:
...
✅Addinganimaltothemap.Total animals:10
...
❌Removinganimalfromthemap.Total animals:0
...
✅Addinganimaltothemap.Total animals:10This is expected. Strict Mode confirms that the ref callbacks are cleaned up correctly, so the size never grows above the expected amount. After the fix, there are no memory leaks, and all the features work as expected.
Without Strict Mode, it was easy to miss the bug until you clicked around to app to notice broken features. Strict Mode made the bugs appear right away, before you push them to production.
Fixing deprecation warnings enabled by Strict Mode
React warns if some component anywhere inside a<StrictMode> tree uses one of these deprecated APIs:
UNSAFE_class lifecycle methods likeUNSAFE_componentWillMount.See alternatives.
These APIs are primarily used in olderclass components so they rarely appear in modern apps.