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.
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.
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);New Root API creates a root with createRoot and then calls render on it.
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" />);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:
import * as ReactDOM from 'react-dom';
import App from 'App';
const container = document.getElementById('app');
// Render with hydration.
ReactDOM.hydrate(<App tab="home" />, container);New version:
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 hereNote 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.
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');
});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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
