Skip to content

React 18: Migrating from ReactDOM.render to createRoot

Problem

React 18 introduced significant changes to the rendering API, deprecating the traditional ReactDOM.render() method. When using Create React App or updating existing projects, developers encounter this warning:

WARNING

Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17.

This occurs because the default index.js file generated by older versions of Create React App uses the deprecated API:

javascript
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

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

Solution

React 18 replaces ReactDOM.render() with a new root API using ReactDOM.createRoot(). This change enables concurrent features and performance improvements in React 18.

Basic Migration

Update your index.js file with the new API:

javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

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

reportWebVitals();

Alternative Syntax

For a more concise approach:

javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

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

TypeScript Version

If using TypeScript, add proper type assertions:

javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

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

reportWebVitals();

With React Router

If your app uses React Router, the migration is similar:

javascript
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

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

Using Named Imports

You can also use named imports for a cleaner approach:

javascript
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const rootElement = document.getElementById('root');
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

Key Differences

The new root API introduces several important changes:

  1. Concurrent Features: createRoot() enables concurrent rendering features
  2. Error Handling: Improved error handling with error boundaries
  3. Hydration: Separate hydrateRoot() method for server-side rendering
  4. Unmounting: New root.unmount() method instead of ReactDOM.unmountComponentAtNode()

INFO

While React 18 maintains backward compatibility with ReactDOM.render(), it runs in a compatibility mode that doesn't support new features. For optimal performance and access to concurrent features, migrate to createRoot().

Deprecated APIs

React 18 deprecated several APIs alongside ReactDOM.render():

  • ReactDOM.hydrate
  • ReactDOM.unmountComponentAtNode
  • ReactDOM.renderSubtreeIntoContainer
  • ReactDOMServer.renderToNodeStream

Testing Library Compatibility

If you encounter issues with testing libraries, ensure you're using compatible versions:

bash
npm i @testing-library/react@latest

Conclusion

Migrating from ReactDOM.render() to createRoot() is straightforward and essential for taking full advantage of React 18's performance improvements and new features. The change primarily involves:

  1. Updating the import from react-dom to react-dom/client
  2. Creating a root with ReactDOM.createRoot()
  3. Using root.render() instead of ReactDOM.render()

This migration future-proofs your application and ensures access to React's latest capabilities while eliminating deprecation warnings.