Frontend Development 7 min read

React 18: Legacy render vs New createRoot – What’s the Difference?

This article explains the differences between React 18’s legacy ReactDOM.render root API and the new ReactDOM.createRoot API, covering concepts of roots, hydration, rendering callbacks, and why both APIs are maintained, with code examples for each approach.

KooFE Frontend Team
KooFE Frontend Team
KooFE Frontend Team
React 18: Legacy render vs New createRoot – What’s the Difference?

Overview

React 18 provides two root APIs, called Legacy Root API and New Root API.

Legacy Root API uses ReactDOM.render , works like React 17, and will be deprecated.

New Root API uses ReactDOM.createRoot , enabling all new React 18 features.

What is a root?

In React, a "root" is a pointer to the top‑level data structure that React uses to track the tree to render.

Legacy root is opaque to the user; it is attached to a DOM element.

<code>import * as ReactDOM from 'react-dom';
import App from 'App';

const container = document.getElementById('app');

// Initial render.
ReactDOM.render(<App tab="home" />, container);

// During an update, React would access
// the root of the DOM element.
ReactDOM.render(<App tab="profile" />, container);
</code>

New Root API creates a root with

createRoot

and then calls

render

on it.

<code>import * as ReactDOM from 'react-dom';
import App from 'App';

const container = document.getElementById('app');

// Create a root.
const root = ReactDOM.createRoot(container);

// Initial render: Render an element to the root.
root.render(<App tab="home" />);

// During an update, there's no need to pass the container again.
root.render(<App tab="profile" />);
</code>

What are the differences?

Reasons for the change include fixing ergonomics of passing the container on each render and removing the need for a separate

hydrate

method, replacing it with an option on the root.

Note: This change allows removing the hydrate method and replacing it with a root option; however, the latest versions deprecate hydrate: true on createRoot and introduce hydrateRoot instead.

What is hydration?

The

hydrate

function has moved to the

hydrateRoot

API.

Old version:

<code>import * as ReactDOM from 'react-dom';
import App from 'App';

const container = document.getElementById('app');

// Render with hydration.
ReactDOM.hydrate(<App tab="home" />, container);
</code>

New version:

<code>import * as ReactDOM from 'react-dom';
import App from 'App';

const container = document.getElementById('app');

// Create *and* render a root with hydration.
const root = ReactDOM.hydrateRoot(container, <App tab="home" />);
// Unlike with createRoot, you don't need a separate root.render() call here
</code>

Note that

hydrateRoot

accepts JSX as its second argument because the initial client render must match the server tree.

What is a render callback?

Legacy Root API allowed passing a callback to

render

that runs after rendering.

<code>import * as ReactDOM from 'react-dom';
import App from 'App';

const container = document.getElementById('app');
ReactDOM.render(<App tab="home" />, container, function() {
  // Called after initial render or any update.
  console.log('rendered');
});
</code>

New Root API removed this callback; developers should use alternatives such as

requestIdleCallback

,

setTimeout

, or

ref

callbacks.

Why support both APIs?

React 18 keeps the Legacy Root API for smooth upgrades and for experimentation, allowing developers to compare performance between the two.

frontend developmentReactReact 18createRoothydrateRoot
KooFE Frontend Team
Written by

KooFE Frontend Team

Follow the latest frontend updates

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.