How to fix ReactDOM.render is deprecated and will be removed in React

ReactBEGINNERMEDIUMUpdated Dec 8, 2025ยท 145 views

ReactDOM.render was deprecated in React 18 and will be removed in React 19. This warning appears when using the legacy rendering API instead of the new createRoot API. Migrating to createRoot unlocks concurrent rendering features and ensures compatibility with future React versions.

Quick fix

Fixes ReactDOM.render is deprecated and will be removed

// Before (React 17 and earlier)
import ReactDOM from 'react-dom';

// After (React 18+)
import { createRoot } from 'react-dom/client';
  1. Import createRoot from react-dom/client
  2. Replace ReactDOM.render with createRoot
  3. Update server-side rendering with hydrateRoot
  4. Update test files to use createRoot
react โ€” error
// Before (React 17 and earlier)
import ReactDOM from 'react-dom';
// After (React 18+)
import { createRoot } from 'react-dom/client';
ReactDOM.render is deprecated and will be removed

What this error means

This deprecation warning indicates you're using the legacy ReactDOM.render() API introduced in React 16, which is incompatible with React 18's concurrent rendering architecture. When React 18 was released in March 2022, the rendering system was redesigned to support concurrent features like automatic batching, transitions, and Suspense improvements. The old ReactDOM.render() creates a "legacy root" that runs in React 17 compatibility mode, preventing your app from accessing any React 18 features. The new createRoot API from 'react-dom/client' creates a modern root that enables the concurrent renderer. This renderer can interrupt, pause, resume, or abandon rendering work, making your UI more responsive. While ReactDOM.render() still works in React 18 with a warning, it will be completely removed in React 19, making migration mandatory. The warning message "your app will behave as if it's running React 17" means you're missing out on performance improvements, automatic batching of state updates, and the ability to use concurrent features that make React applications faster and more efficient.

Typical symptoms

  • Console warning: "ReactDOM.render is no longer supported in React 18. Use createRoot instead."
  • App continues to work but runs in React 17 compatibility mode
  • React DevTools shows the app running in legacy mode
  • Automatic batching and concurrent features are unavailable
  • Build warnings in CI/CD pipelines when using React 18+
  • Type errors when trying to use React 18-specific features like useTransition or useDeferredValue
  • ESLint warnings if using react/no-deprecated rules

Common causes

  1. Using ReactDOM.render() in your entry point (index.js/main.tsx) with React 18 or later
  2. Upgrading from React 17 to React 18 without migrating the root API
  3. Following outdated tutorials or boilerplates that use the legacy rendering method
  4. Third-party libraries or component wrappers still using ReactDOM.render internally
  5. Test files using ReactDOM.render instead of the testing library's createRoot
  6. Server-side rendering code using the old hydrate() API instead of hydrateRoot()
  7. Copy-pasting code from pre-React 18 examples or documentation

How to fix "ReactDOM.render is deprecated and will be removed"

1Import createRoot from react-dom/client

Replace the legacy import with the new client-side rendering API:

javascript
// Before (React 17 and earlier)
import ReactDOM from 'react-dom';

// After (React 18+)
import { createRoot } from 'react-dom/client';

For TypeScript projects, ensure you have @types/react-dom version 18.0.0 or later installed.

2Replace ReactDOM.render with createRoot

Update your entry point file (typically index.js, index.tsx, or main.tsx):

javascript
// Before (React 17)
import ReactDOM from 'react-dom';
import App from './App';

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

// After (React 18)
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
const root = createRoot(container);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

The key difference is that createRoot() returns a root object, and you call render() on that root rather than passing the container directly to ReactDOM.render().

3Update server-side rendering with hydrateRoot

If your app uses SSR, replace ReactDOM.hydrate() with hydrateRoot():

javascript
// Before (React 17)
import ReactDOM from 'react-dom';

ReactDOM.hydrate(
  <App />,
  document.getElementById('root')
);

// After (React 18)
import { hydrateRoot } from 'react-dom/client';

const container = document.getElementById('root');
hydrateRoot(container, <App />);

Note that hydrateRoot doesn't return a root object like createRoot does - it immediately hydrates the container.

4Update test files to use createRoot

Replace ReactDOM.render in test files with the new API:

javascript
// Before
import ReactDOM from 'react-dom';

test('renders component', () => {
  const container = document.createElement('div');
  ReactDOM.render(<MyComponent />, container);
  expect(container.textContent).toBe('Hello');
});

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

test('renders component', () => {
  const container = document.createElement('div');
  const root = createRoot(container);
  root.render(<MyComponent />);
  expect(container.textContent).toBe('Hello');
});

If using React Testing Library, upgrade to version 13+ which uses createRoot automatically.

5Handle null container safety

Add proper null checks since getElementById can return null:

typescript
// TypeScript-safe version
const container = document.getElementById('root');
if (!container) {
  throw new Error('Failed to find the root element');
}
const root = createRoot(container);
root.render(<App />);

This prevents TypeScript errors and runtime crashes if the DOM element doesn't exist.

6Update unmount calls to use root.unmount

If you ever unmount the root (common in tests or dynamic SPAs):

javascript
// Before
ReactDOM.unmountComponentAtNode(container);

// After
root.unmount();

The unmount method is now called on the root instance rather than the container.

7Verify React version compatibility

Ensure all React packages are version 18.0.0 or later:

bash
npm list react react-dom
# or
yarn list react react-dom

If versions are mismatched, update both packages:

bash
npm install react@latest react-dom@latest
# or
yarn add react@latest react-dom@latest

Mismatched versions (e.g., React 18 with react-dom 17) will cause errors.

Advanced notes

Concurrent Rendering Benefits: After migrating to createRoot, your app gains access to concurrent features like automatic batching (multiple setState calls in event handlers are batched into a single re-render), useTransition for marking updates as non-urgent, and useDeferredValue for deferring expensive computations. These features significantly improve perceived performance without code changes.

Backward Compatibility: If you're maintaining a library that needs to support both React 17 and 18, you can conditionally use the appropriate API:

javascript
import * as ReactDOM from 'react-dom';

const render = (app, container) => {
  if (ReactDOM.createRoot) {
    // React 18
    const root = ReactDOM.createRoot(container);
    root.render(app);
    return root;
  } else {
    // React 17 and earlier
    ReactDOM.render(app, container);
  }
};

Callback Removal: ReactDOM.render() accepted an optional callback as a third argument that fired after rendering. createRoot's render() does not support callbacks. Use useEffect in a component or flushSync for synchronous updates instead.

Strict Mode Behavior: React 18's Strict Mode intentionally double-invokes effects in development to help find bugs. This is unrelated to the createRoot migration but may cause confusion when migrating older codebases.

Third-Party Library Issues: Some libraries (Chakra UI, Material-UI, testing libraries) had their own wrappers around ReactDOM.render. Check their migration guides for React 18 compatibility or update to versions that support createRoot natively.

Create React App: CRA version 5.0.1+ automatically uses createRoot. If you're on an older CRA version, either upgrade or manually eject and update the entry point.

Prevention: When starting new projects, use create-react-app@latest, Next.js 12.2+, or Vite's React template, all of which default to createRoot. Always refer to the official React documentation rather than outdated tutorials.

Frequently asked questions

What does "ReactDOM.render is deprecated and will be removed" mean?
render() API introduced in React 16, which is incompatible with React 18's concurrent rendering architecture. When React 18 was released in March 2022, the rendering system was redesigned to support concurrent features like automatic batching, transitions, and Suspense improvements. render() creates a "legacy root" that runs in React 17 compatibility mode, preventing your app from accessing any React 18 features.
How do I fix "ReactDOM.render is deprecated and will be removed"?
ReactDOM.render was deprecated in React 18 and will be removed in React 19. This warning appears when using the legacy rendering API instead of the new createRoot API. Migrating to createRoot unlocks concurrent rendering features and ensures compatibility with future React versions.
What causes "ReactDOM.render is deprecated and will be removed"?
Using ReactDOM.render() in your entry point (index.js/main.tsx) with React 18 or later Upgrading from React 17 to React 18 without migrating the root API Following outdated tutorials or boilerplates that use the legacy rendering method
Is the "ReactDOM.render is deprecated and will be removed" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading