createRoot
createRoot
lets you create a root to display React components inside a browser DOM node.
const root = createRoot(domNode, options?)
Reference
createRoot(domNode, options?)
Call createRoot
to create a React root for displaying content inside a browser DOM element.
import { createRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = createRoot(domNode);
React will create a root for the domNode
, and take over managing the DOM inside it. After you’ve created a root, you need to call root.render
to display a React component inside of it:
root.render(<App />);
An app fully built with React will usually only have one createRoot
call for its root component. A page that uses “sprinkles” of React for parts of the page may have as many separate roots as needed.
Parameters
-
domNode
: A DOM element. React will create a root for this DOM element and allow you to call functions on the root, such asrender
to display rendered React content. -
optional
options
: An object with options for this React root.- optional
onCaughtError
: Callback called when React catches an error in an Error Boundary. Called with theerror
caught by the Error Boundary, and anerrorInfo
object containing thecomponentStack
. - optional
onUncaughtError
: Callback called when an error is thrown and not caught by an Error Boundary. Called with theerror
that was thrown, and anerrorInfo
object containing thecomponentStack
. - optional
onRecoverableError
: Callback called when React automatically recovers from errors. Called with anerror
React throws, and anerrorInfo
object containing thecomponentStack
. Some recoverable errors may include the original error cause aserror.cause
. - optional
identifierPrefix
: A string prefix React uses for IDs generated byuseId
. Useful to avoid conflicts when using multiple roots on the same page.
- optional
Returns
createRoot
returns an object with two methods: render
and unmount
.
Caveats
- If your app is server-rendered, using
createRoot()
is not supported. UsehydrateRoot()
instead. - You’ll likely have only one
createRoot
call in your app. If you use a framework, it might do this call for you. - When you want to render a piece of JSX in a different part of the DOM tree that isn’t a child of your component (for example, a modal or a tooltip), use
createPortal
instead ofcreateRoot
.
root.render(reactNode)
Call root.render
to display a piece of JSX (“React node”) into the React root’s browser DOM node.
root.render(<App />);
React will display <App />
in the root
, and take over managing the DOM inside it.
Parameters
reactNode
: A React node that you want to display. This will usually be a piece of JSX like<App />
, but you can also pass a React element constructed withcreateElement()
, a string, a number,null
, orundefined
.
Returns
root.render
returns undefined
.
Caveats
-
The first time you call
root.render
, React will clear all the existing HTML content inside the React root before rendering the React component into it. -
If your root’s DOM node contains HTML generated by React on the server or during the build, use
hydrateRoot()
instead, which attaches the event handlers to the existing HTML. -
If you call
render
on the same root more than once, React will update the DOM as necessary to reflect the latest JSX you passed. React will decide which parts of the DOM can be reused and which need to be recreated by “matching it up” with the previously rendered tree. Callingrender
on the same root again is similar to calling theset
function on the root component: React avoids unnecessary DOM updates.
root.unmount()
Call root.unmount
to destroy a rendered tree inside a React root.
root.unmount();
An app fully built with React will usually not have any calls to root.unmount
.
This is mostly useful if your React root’s DOM node (or any of its ancestors) may get removed from the DOM by some other code. For example, imagine a jQuery tab panel that removes inactive tabs from the DOM. If a tab gets removed, everything inside it (including the React roots inside) would get removed from the DOM as well. In that case, you need to tell React to “stop” managing the removed root’s content by calling root.unmount
. Otherwise, the components inside the removed root won’t know to clean up and free up global resources like subscriptions.
Calling root.unmount
will unmount all the components in the root and “detach” React from the root DOM node, including removing any event handlers or state in the tree.
Parameters
root.unmount
does not accept any parameters.
Returns
root.unmount
returns undefined
.
Caveats
-
Calling
root.unmount
will unmount all the components in the tree and “detach” React from the root DOM node. -
Once you call
root.unmount
you cannot callroot.render
again on the same root. Attempting to callroot.render
on an unmounted root will throw a “Cannot update an unmounted root” error. However, you can create a new root for the same DOM node after the previous root for that node has been unmounted.
Usage
Rendering an app fully built with React
If your app is fully built with React, create a single root for your entire app.
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
Usually, you only need to run this code once at startup. It will:
- Find the browser DOM node defined in your HTML.
- Display the React component for your app inside.
import { createRoot } from 'react-dom/client'; import App from './App.js'; import './styles.css'; const root = createRoot(document.getElementById('root')); root.render(<App />);
If your app is fully built with React, you shouldn’t need to create any more roots, or to call root.render
again.
From this point on, React will manage the DOM of your entire app. To add more components, nest them inside the App
component. When you need to update the UI, each of your components can do this by using state. When you need to display extra content like a modal or a tooltip outside the DOM node, render it with a portal.
Rendering a page partially built with React
If your page isn’t fully built with React, you can call createRoot
multiple times to create a root for each top-level piece of UI managed by React. You can display different content in each root by calling root.render
.
Here, two different React components are rendered into two DOM nodes defined in the index.html
file:
import './styles.css'; import { createRoot } from 'react-dom/client'; import { Comments, Navigation } from './Components.js'; const navDomNode = document.getElementById('navigation'); const navRoot = createRoot(navDomNode); navRoot.render(<Navigation />); const commentDomNode = document.getElementById('comments'); const commentRoot = createRoot(commentDomNode); commentRoot.render(<Comments />);
You could also create a new DOM node with document.createElement()
and add it to the document manually.
const domNode = document.createElement('div');
const root = createRoot(domNode);
root.render(<Comment />);
document.body.appendChild(domNode); // You can add it anywhere in the document
To remove the React tree from the DOM node and clean up all the resources used by it, call root.unmount
.
root.unmount();
This is mostly useful if your React components are inside an app written in a different framework.
Updating a root component
You can call render
more than once on the same root. As long as the component tree structure matches up with what was previously rendered, React will preserve the state. Notice how you can type in the input, which means that the updates from repeated render
calls every second in this example are not destructive:
import { createRoot } from 'react-dom/client'; import './styles.css'; import App from './App.js'; const root = createRoot(document.getElementById('root')); let i = 0; setInterval(() => { root.render(<App counter={i} />); i++; }, 1000);
It is uncommon to call render
multiple times. Usually, your components will update state instead.
Show a dialog for uncaught errors
By default, React will log all uncaught errors to the console. To implement your own error reporting, you can provide the optional onUncaughtError
root option:
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onUncaughtError: (error, errorInfo) => {
console.error(
'Uncaught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
The onUncaughtError option is a function called with two arguments:
- The error that was thrown.
- An errorInfo object that contains the componentStack of the error.
You can use the onUncaughtError
root option to display error dialogs:
import { createRoot } from "react-dom/client"; import App from "./App.js"; import {reportUncaughtError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = createRoot(container, { onUncaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportUncaughtError({ error, componentStack: errorInfo.componentStack }); } } }); root.render(<App />);
Displaying Error Boundary errors
By default, React will log all errors caught by an Error Boundary to console.error
. To override this behavior, you can provide the optional onCaughtError
root option to handle errors caught by an Error Boundary:
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onCaughtError: (error, errorInfo) => {
console.error(
'Caught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
The onCaughtError option is a function called with two arguments:
- The error that was caught by the boundary.
- An errorInfo object that contains the componentStack of the error.
You can use the onCaughtError
root option to display error dialogs or filter known errors from logging:
import { createRoot } from "react-dom/client"; import App from "./App.js"; import {reportCaughtError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = createRoot(container, { onCaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportCaughtError({ error, componentStack: errorInfo.componentStack, }); } } }); root.render(<App />);
Displaying a dialog for recoverable errors
React may automatically render a component a second time to attempt to recover from an error thrown in render. If successful, React will log a recoverable error to the console to notify the developer. To override this behavior, you can provide the optional onRecoverableError
root option:
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onRecoverableError: (error, errorInfo) => {
console.error(
'Recoverable error',
error,
error.cause,
errorInfo.componentStack,
);
}
}
);
root.render(<App />);
The onRecoverableError option is a function called with two arguments:
- The error that React throws. Some errors may include the original cause as error.cause.
- An errorInfo object that contains the componentStack of the error.
You can use the onRecoverableError
root option to display error dialogs:
import { createRoot } from "react-dom/client"; import App from "./App.js"; import {reportRecoverableError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = createRoot(container, { onRecoverableError: (error, errorInfo) => { reportRecoverableError({ error, cause: error.cause, componentStack: errorInfo.componentStack, }); } }); root.render(<App />);
Troubleshooting
I’ve created a root, but nothing is displayed
Make sure you haven’t forgotten to actually render your app into the root:
import { createRoot } from 'react-dom/client';
import App from './App.js';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
Until you do that, nothing is displayed.
I’m getting an error: “You passed a second argument to root.render”
A common mistake is to pass the options for createRoot
to root.render(...)
:
To fix, pass the root options to createRoot(...)
, not root.render(...)
:
// 🚩 Wrong: root.render only takes one argument.
root.render(App, {onUncaughtError});
// ✅ Correct: pass options to createRoot.
const root = createRoot(container, {onUncaughtError});
root.render(<App />);
I’m getting an error: “Target container is not a DOM element”
This error means that whatever you’re passing to createRoot
is not a DOM node.
If you’re not sure what’s happening, try logging it:
const domNode = document.getElementById('root');
console.log(domNode); // ???
const root = createRoot(domNode);
root.render(<App />);
For example, if domNode
is null
, it means that getElementById
returned null
. This will happen if there is no node in the document with the given ID at the time of your call. There may be a few reasons for it:
- The ID you’re looking for might differ from the ID you used in the HTML file. Check for typos!
- Your bundle’s
<script>
tag cannot “see” any DOM nodes that appear after it in the HTML.
Another common way to get this error is to write createRoot(<App />)
instead of createRoot(domNode)
.
I’m getting an error: “Functions are not valid as a React child.”
This error means that whatever you’re passing to root.render
is not a React component.
This may happen if you call root.render
with Component
instead of <Component />
:
// 🚩 Wrong: App is a function, not a Component.
root.render(App);
// ✅ Correct: <App /> is a component.
root.render(<App />);
Or if you pass a function to root.render
, instead of the result of calling it:
// 🚩 Wrong: createApp is a function, not a component.
root.render(createApp);
// ✅ Correct: call createApp to return a component.
root.render(createApp());
My server-rendered HTML gets re-created from scratch
If your app is server-rendered and includes the initial HTML generated by React, you might notice that creating a root and calling root.render
deletes all that HTML, and then re-creates all the DOM nodes from scratch. This can be slower, resets focus and scroll positions, and may lose other user input.
Server-rendered apps must use hydrateRoot
instead of createRoot
:
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(
document.getElementById('root'),
<App />
);
Note that its API is different. In particular, usually there will be no further root.render
call.