DEV Community

Cover image for ReactDOM.createRoot 🆚 ReactDOM.render
Nick
Nick

Posted on

7 3

ReactDOM.createRoot 🆚 ReactDOM.render

What is ReactDOM.createRoot and how is it different from ReactDOM.render?

React 18 introduces a new root API, so let's figure it out

👉 Current API

We have to pass the container to render function every time we want to explicitly render.

const container = document.querySelector('#root');

// Initial render. Container is explicitly accessed.
ReactDOM.render(<App text="Hello" />, container);

// Subsequent renders. Container is explicitly accessed.
ReactDOM.render(<App text="Hello world!" />, container);
Enter fullscreen mode Exit fullscreen mode

👉 What does ReactDOM.render take?

render function takes three arguments:

  • React element to be rendered
  • DOM element to render in
  • function to be executed after render happens

And returns the same container, but with the rendered component.

/**
* @param element - React element to be rendered
* @param container - DOM element to render in
* @param callback - function to be executed after render happens
* @return container - container with renderned component
*/
function render(element, container, callback) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

👉 How does ReactDOM.render work under the hood?

ReactDOM.render does a couple of validation checks:

  • whether the container is a suitable node
  • whether the container wasn't previously passed to createRoot

Then it passes all received arguments to legacyRenderSubtreeIntoContainer.

// simplified structure
function render(element, container, callback) {
  if (isValidContainer(element)) {
    throw Error('Target container is not a DOM element.');
  }

  if (isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined) {
    // don't throw an error, but logs it into console
    error('container was previously passed to ReactDOM.createRoot().');
  }

  return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
}
Enter fullscreen mode Exit fullscreen mode

👉 New API

It fixes the issue of passing the container every time we want to explicitly render.

// First, we create a root
const root = ReactDOM.createRoot(document.querySelector('#root'));

// Initial render. Container is implicitly accessed.
root.render(<App name="Hello" />);

// Subsequent renders. Container is implicitly accessed.
root.render(<App name="Hello world!" />);
Enter fullscreen mode Exit fullscreen mode

👉 What does ReactDOM.createRoot take?

createRoot function takes only one mandatory argument - DOM element to render in.
And returns RootType, which has render and unmount methods.

P.S. Also createRoot takes the second RootOptions argument, but we'll examine it in the future.

/**
* @param container - DOM element to render in
* @param options - options, related to hydration
* @return RootType - instance of root
*/
function createRoot(container, options) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

👉 How does ReactDOM.createRoot work under the hood?

On top of the render function, createRoot:

  • checks whether the container isn't a body element
  • provide a bit more detailed warnings

Then createRoot instance a new ReactDOMRoot object and returns it. No legacyRenderSubtreeIntoContainer at all!

// simplified structure
function createRoot(container, options) {
  if (isValidContainer(element)) {
    throw Error('Target container is not a DOM element.');
  }

  if (container.nodeType === 1 && container.tagName.toUpperCase() === 'BODY') {
    console.error('Creating roots directly with document.body is discouraged');
  }

  if (isContainerMarkedAsRoot(container) {
    if (container._reactRootContainer) {
      console.error('container was previously passed to ReactDOM.render().')
    } else {
      console.error('container has already been passed to createRoot() before.');
    }
  }

  return new ReactDOMRoot(container, options);
}
Enter fullscreen mode Exit fullscreen mode

Now you know the underline difference between old render and new createRoot functions!

Let me know, if you want a further comparison of legacyRenderSubtreeIntoContainer and new ReactDOMRoot.

P.S. Follow me on Twitter for more content like this!

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (2)

Collapse
 
jaewangl profile image
JaeWang Lee • Edited

So useful!

Collapse
 
fromaline profile image
Nick

Thanks 🙏🏻
I appreciate this kind of feedback a lot!

Cloudinary image

Video API: manage, encode, and optimize for any device, channel or network condition. Deliver branded video experiences in minutes and get deep engagement insights.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay