facebook/react · error · Error
509
509
Error message
ReactDOM: Unsupported Legacy Mode API.
What it means
ReactDOM.render is the legacy root API. With the disableLegacyMode flag (React 19 builds) it logs a migration note in DEV and then throws 'ReactDOM: Unsupported Legacy Mode API.' (code 509), making removal a hard error. The replacement is createRoot(container).render(element); the old callback second argument becomes an effect. Note this throw fires before the container validity check, so a bad container never reaches the code-299 error in this build.
Source
Thrown at packages/react-dom/src/client/ReactDOMRootFB.js:399
}
if (__DEV__) {
return findHostInstanceWithWarning(componentOrElement, 'findDOMNode');
}
return findHostInstance(componentOrElement);
}
export function render(
element: React$Element<any>,
container: Container,
callback: ?Function,
): component(...props: any) | PublicInstance | null {
if (disableLegacyMode) {
if (__DEV__) {
console.error(
'ReactDOM.render was removed in React 19. Use createRoot instead.',
);
}
throw new Error('ReactDOM: Unsupported Legacy Mode API.');
}
if (__DEV__) {
console.error(
'ReactDOM.render has not been supported since React 18. Use createRoot ' +
'instead. Until you switch to the new API, your app will behave as ' +
"if it's running React 17. Learn " +
'more: https://react.dev/link/switch-to-createroot',
);
}
if (!isValidContainer(container)) {
throw new Error('Target container is not a DOM element.');
}
if (__DEV__) {
const isModernRoot =
isContainerMarkedAsRoot(container) &&
container._reactRootContainer === undefined;View on GitHub (pinned to eafeac097b)
Solutions
- Replace ReactDOM.render(el, container, cb) with createRoot(container).render(<App />) and move the callback into a useEffect
- Replace ReactDOM.hydrate with hydrateRoot(container, initialChildren)
- Run the React 19 upgrade codemod and search the repo for ReactDOM.render leftovers
- Update or replace dependencies that internally call ReactDOM.render
Example fix
// before
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// after
import {createRoot} from 'react-dom/client';
createRoot(document.getElementById('root')).render(<App />); Defensive patterns
Strategy: fallback
Validate before calling
import * as ReactDOM from 'react-dom';
import {version} from 'react';
const isLegacyRemoved = Number(version.split('.')[0]) >= 19;
function mountApp(element, container) {
if (isLegacyRemoved) {
// React 19+: modern API
import('react-dom/client').then(({createRoot}) => {
createRoot(container).render(element);
});
} else {
ReactDOM.render(element, container);
}
} Prevention
- Grep for ReactDOM.render and ReactDOM.hydrate during React 19 upgrades and migrate all call sites
- Replace the callback argument with a useEffect inside the root component
- Add an ESLint rule or codemod run in CI to block reintroduction of legacy root APIs
When it happens
Trigger: Calling ReactDOM.render(<App />, container, callback?) (or ReactDOM.hydrate) on a build where disableLegacyMode is true — i.e. React 19 — including via old tutorials, legacy wrappers, or dependencies that still call it.
Common situations: Upgrading React 17/18 codebases to 19 without migrating entry points; libraries wrapping ReactDOM.render (legacy modal or widget managers); call sites missed by upgrade codemods.
Related errors
- 299
- 426
- react-test-renderer/shallow has been removed. See https://re
- Did not expect a host hoistable to be the root
- 62
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/6da11e629e7ff6b6.
Report an issue: GitHub.