In my react-based library, I was using ReactDOM.render at 3 different levels. The first level is at the root level and I am clear and replaced it using the below code:

import { createRoot } from 'react-dom/client';
    
const root = createRoot(domElement);
root.render(reactElement);

For other two levels (children of root), I want to render a certain Component in a designated DOM element. If I am using:

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

const root = createRoot(childDomElement);
root.render(reactElement);

I am getting the following warning:

You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.

What is the right way to render a Component in a particular DOM element?

8

There are 8 best solutions below

0
On

The answer is inside the warning itself.

You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before.

The root cause of the warning at my end is that the same DOM element is used to create the root more than once.

To overcome the issue it is to be sure that the createRoot is called only once on one DOM element and after that root.render(reactElement); can be used to update and createRoot should not be used.

1
On

It also happen to me. For me, it because DOMContentLoaded callback triggered twice.

My fix just make sure the container rendered only once.

let container = null;

document.addEventListener('DOMContentLoaded', function(event) {
  if (!container) {
    container = document.getElementById('root1') as HTMLElement;
    const root = createRoot(container)
    root.render(
      <React.StrictMode>
        <h1>ZOO</h1>
      </React.StrictMode>
    );
  }
});
0
On

The typical way to trigger this warning is to call createRoot multiple times on the same root:

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<p>A</p>);
ReactDOM.createRoot(document.querySelector("#app"))
  .render(<p>B</p>);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

You can see that the B render overwrites the A render.

If you want to run two different React apps, use different roots:

ReactDOM.createRoot(document.querySelector("#app-a"))
  .render(<p>A</p>);
ReactDOM.createRoot(document.querySelector("#app-b"))
  .render(<p>B</p>);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app-a"></div>
<div id="app-b"></div>

Otherwise, if you're trying to render different components in the same app, render once with a single root component and render child components inside the root's subtree. This is the common case; most pages use one React app.

In most cases, createRoot acts as an up front, one time set up for the whole React app that persists for the duration of the user's visit to the page. Rendering conditions go inside the component tree, and the element you want to render in is specified in the component as JSX.

For example:

const A = () => <p>A</p>;
const B = () => <p>B</p>;
const App = () => <React.Fragment><A /><B /></React.Fragment>;

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

If, for some reason, you want to render into that root multiple times, you can, as long as you limit yourself to one createRoot call per root:

const root = ReactDOM.createRoot(document.querySelector("#app"));
root.render(<p>A</p>);

// silly example
setTimeout(() => root.render(<p>B</p>), 2000);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

Another possibility is you're looking for a portal that lets you escape the React DOM tree and render a component elsewhere. As above, though, there's stll one createRoot and one render and we use portals to put the component in a specfic element.

For example:

const A = () => <p>A</p>;
const B = () => ReactDOM.createPortal(
  <p>B</p>,
  document.querySelector("#portal")
);
const App = () => <React.Fragment><A /><B /></React.Fragment>;

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
<div id="portal"></div>

Portals are often seen in modals.


For completeness, another uncommon way to trigger this is by including Babel twice, as can occur in a Stack Snippet that has babel: true as well as an import:

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<p>test</p>);
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

Which is more or less the same as:

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
<script type="text/babel">
ReactDOM.createRoot(document.querySelector("#app"))
  .render(<p>test</p>);
</script>

0
On

I had the similar scenario,I had to render the another component on button click from current component. I was able to achieve this successfully by following code.

import React  from "react";
import  ReactDOM  from "react-dom/client";
import reportWebVitals from "./reportWebVitals"; 

class Lifecycle extends React.Component{ 
  showReports=()=>{  
    root.render(<Reports></Reports>);    
  }
  render() {
    return (
      <div>
        <h2>Welcome to Component Lifecycle..</h2>        
        <button onClick={this.showReports}>Show Reports</button>
        </div>
      );
    }
}

class Report extends React.Component{ 
  
  render() {
    return (
      <div>
        <h2>Welcome to Report Component..</h2>                
        </div>
      );
    }
}


const element=<Lifecycle></Lifecycle>;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element);
reportWebVitals();
0
On

I had this issue, i was using Rollup to bundle a complicated NPM package that was in the same directory as our Storybook app AND Create-React-App demo page.

It turned out we were bundling the Create-React-App index.js file, which contained createRoot() already. This means it was trying to attach the react app from the package along with our react app.

I solved this by NOT bundling the index.js file that we were using to demo our components and made a 'package-index.js' file that did not contain createRoot(), specifically for the package.

Hope this helps someone :)

0
On

I solved it by a foolish mistake having the old parameter still there:

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

The last one should not be there document.getElementById('root'),

0
On

You're likely importing something from your entrypoint file, causing the entry point file to somehow run twice. I've had the same issue and solved it by making sure I was not importing anything from my entrypoint file.

3
On

There may be others who encounter the same error as I do while using the 'react-router-dom' package.

Remove the <App /> (or main component) component and instead call the <RouterProvider /> component inside the .render() function. Like this:

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

should be replaced with

ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={mainRouter} />)

The issue arises when you invoke both <RouterProvider> and .createRoot on the same page. To resolve this problem, instead of directly rendering the <RouterProvider> component, you should import and invoke either the <App> or <RouterProvider> component to address this issue.

RouterProvider Docs