This React warning occurs when you try to unmount a component from a DOM node that wasn't rendered by React, or when you're trying to unmount a React root node instead of its container. It's a common issue when managing component lifecycles manually or when working with third-party libraries that manipulate the DOM independently of React.
The "unmountComponentAtNode(): target is not valid" warning is React's way of telling you that you're trying to unmount a component from a DOM node that either: 1. Wasn't originally rendered by React (it might have been rendered by vanilla JavaScript or another library) 2. Is a React root node itself, rather than the container that holds the root node 3. Was rendered by a different copy of React (in cases where multiple React bundles are loaded) This warning was introduced in React 16 to prevent developers from accidentally unmounting the wrong nodes, which could lead to memory leaks or unexpected behavior. In React 15 and earlier, such operations would silently fail or cause subtle bugs.
Make sure you're passing the exact same DOM node to ReactDOM.unmountComponentAtNode() that you originally passed to ReactDOM.render(). The container should be a plain DOM element, not a React component or a node inside the React-rendered tree.
// Correct usage
const container = document.getElementById('root');
ReactDOM.render(<App />, container);
// Later...
ReactDOM.unmountComponentAtNode(container); // Same container
// Incorrect usage
const innerNode = document.querySelector('.inner-component');
ReactDOM.unmountComponentAtNode(innerNode); // This node wasn't the root containerIf you see the warning about "another copy of React", you likely have multiple React bundles loaded. This can happen in monorepos, microfrontends, or when dependencies bundle their own React.
Use your browser's dev tools to check:
// In the console
console.log(React.version);
// Check if different components log different versionsSolutions:
1. Use peerDependencies in your packages to ensure a single React version
2. Configure Webpack or your bundler to alias 'react' and 'react-dom' to a single instance
3. For microfrontends, consider using module federation or a shared dependency strategy
Instead of manually unmounting components, let React handle unmounting through conditional rendering based on state or props.
// Instead of manual unmounting
function ParentComponent() {
const handleUnmount = () => {
// Avoid this pattern
ReactDOM.unmountComponentAtNode(document.getElementById('child-container'));
};
return (
<div>
<button onClick={handleUnmount}>Unmount Child</button>
<div id="child-container"></div>
</div>
);
}
// Use conditional rendering instead
function ParentComponent() {
const [showChild, setShowChild] = useState(true);
return (
<div>
<button onClick={() => setShowChild(false)}>
Unmount Child
</button>
{showChild && <ChildComponent />}
</div>
);
}If you need to render components to different DOM nodes (like modals, tooltips), use React portals instead of manually managing separate React roots.
function Modal({ children }) {
const modalRoot = document.getElementById('modal-root');
// Create portal instead of separate ReactDOM.render
return ReactDOM.createPortal(
children,
modalRoot
);
}
// Usage remains declarative
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button onClick={() => setShowModal(true)}>
Show Modal
</button>
{showModal && (
<Modal>
<div>Modal Content</div>
</Modal>
)}
</div>
);
}Portals are automatically cleaned up when the parent component unmounts.
When working with effects that might need to clean up DOM nodes, make sure to return a cleanup function from your useEffect hook.
function ComponentWithExternalDom() {
useEffect(() => {
// Create some external DOM manipulation
const externalDiv = document.createElement('div');
document.body.appendChild(externalDiv);
// Render React into it
ReactDOM.render(<ExternalContent />, externalDiv);
// Cleanup function
return () => {
ReactDOM.unmountComponentAtNode(externalDiv);
document.body.removeChild(externalDiv);
};
}, []);
return <div>Main component</div>;
}This ensures React properly unmounts the component when your component unmounts.
This warning was introduced in React 16 (specifically through pull request #2065) to address common pitfalls with manual DOM management. Prior to React 16, developers could accidentally pass the wrong node to unmountComponentAtNode(), leading to memory leaks or React getting into an inconsistent state.
The warning system checks several conditions:
1. Whether the node has React internal properties (_reactRootContainer)
2. Whether the node was rendered by the same copy of React (important for microfrontends)
3. Whether the node is actually a top-level container (not a child within a React tree)
In production builds, this warning is stripped out, so it won't affect your users. However, you should still address it in development to ensure your application's architecture is sound.
For large applications migrating from React 15 to 16+, this warning often surfaces architectural issues where components were manually managing their mounting/unmounting instead of letting React handle the lifecycle declaratively.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite