createRoot
Creates a React root for rendering React components into a DOM container with concurrent rendering features.
import { createRoot } from 'react-dom/client' ;
const root = createRoot ( container , options );
Reference
createRoot(container, options?)
Call createRoot to create a React root for displaying content inside a browser DOM element.
/src/client/ReactDOMRoot.js:171-260
const root = createRoot ( document . getElementById ( 'root' ));
root . render ( < App /> );
Parameters
container
Element | Document | DocumentFragment
required
A DOM element, Document, or DocumentFragment that will contain your React app. React will create a root for this container and manage the DOM inside it. Validation:
Must be a valid DOM container
Cannot be a text node or comment node (unless feature flag enabled)
Should not be already managed by another React root
Optional configuration object for the root. onRecoverableError
(error: Error, errorInfo: ErrorInfo) => void
Called when React automatically recovers from errors. Useful for logging errors to an error reporting service. createRoot ( container , {
onRecoverableError : ( error , errorInfo ) => {
console . error ( 'Recoverable error:' , error );
console . log ( 'Component stack:' , errorInfo . componentStack );
}
});
The errorInfo object contains:
componentStack?: string - Stack trace of component hierarchy
onUncaughtError
(error: Error, errorInfo: ErrorInfo) => void
Called when an error is thrown and not caught by any error boundary. createRoot ( container , {
onUncaughtError : ( error , errorInfo ) => {
logErrorToService ( error , errorInfo . componentStack );
}
});
onCaughtError
(error: Error, errorInfo: ErrorInfo) => void
Called when an error boundary catches an error. The errorInfo includes the errorBoundary component reference. createRoot ( container , {
onCaughtError : ( error , errorInfo ) => {
console . log ( 'Caught by:' , errorInfo . errorBoundary );
}
});
String prefix for IDs generated by useId. Useful to avoid conflicts when using multiple roots on the same page. const root1 = createRoot ( container1 , { identifierPrefix: 'app1-' });
const root2 = createRoot ( container2 , { identifierPrefix: 'app2-' });
Enable Strict Mode for this root. Helps identify potential problems by running effects twice in development. createRoot ( container , {
unstable_strictMode: true
});
This is an unstable API. Use <StrictMode> component instead for better compatibility.
unstable_transitionCallbacks
TransitionTracingCallbacks
Callbacks for transition tracing. Experimental API for tracking concurrent transitions.
onDefaultTransitionIndicator
() => void | (() => void)
Called when a transition indicator should be shown. Can return a cleanup function.
Returns
A root object with two methods: render and unmount.
root.render(reactNode)
Renders a React component into the root’s DOM container.
/src/client/ReactDOMRoot.js:107-136
Parameters
A React node to render. Usually JSX like <App />, but can also be a React element created with createElement(), a string, a number, null, or undefined.
Notes
The first call replaces any existing HTML inside the container
Subsequent calls use React’s diffing algorithm to update efficiently
Does not support callback argument (removed in React 18)
Updates are batched and may be asynchronous
root.unmount()
Destroys a rendered React component and cleans up its DOM and event handlers.
/src/client/ReactDOMRoot.js:139-169
Notes
Runs cleanup functions in useEffect and useLayoutEffect
Synchronously completes unmounting before returning
Should not be called while React is already rendering
Container is emptied but not removed from the DOM
Usage Examples
Basic Client Rendering
import { createRoot } from 'react-dom/client' ;
import App from './App' ;
const container = document . getElementById ( 'root' );
const root = createRoot ( container );
root . render ( < App /> );
With Error Handling
import { createRoot } from 'react-dom/client' ;
import * as Sentry from '@sentry/react' ;
const root = createRoot ( document . getElementById ( 'root' ), {
onRecoverableError : ( error , errorInfo ) => {
// Log recoverable errors (hydration mismatches, etc.)
console . warn ( 'Recoverable error:' , error );
Sentry . captureException ( error , {
contexts: {
react: {
componentStack: errorInfo . componentStack ,
},
},
});
},
onUncaughtError : ( error , errorInfo ) => {
// Log uncaught errors
Sentry . captureException ( error , {
contexts: {
react: {
componentStack: errorInfo . componentStack ,
},
},
});
},
onCaughtError : ( error , errorInfo ) => {
// Log errors caught by error boundaries
console . log ( 'Caught by error boundary:' , errorInfo . errorBoundary );
},
});
root . render ( < App /> );
Multiple Roots on Same Page
import { createRoot } from 'react-dom/client' ;
// Create separate roots with unique ID prefixes
const headerRoot = createRoot ( document . getElementById ( 'header' ), {
identifierPrefix: 'header-' ,
});
const sidebarRoot = createRoot ( document . getElementById ( 'sidebar' ), {
identifierPrefix: 'sidebar-' ,
});
const mainRoot = createRoot ( document . getElementById ( 'main' ), {
identifierPrefix: 'main-' ,
});
headerRoot . render ( < Header /> );
sidebarRoot . render ( < Sidebar /> );
mainRoot . render ( < MainContent /> );
Updating the Root
import { createRoot } from 'react-dom/client' ;
import { useState } from 'react' ;
const root = createRoot ( document . getElementById ( 'root' ));
// Initial render
root . render ( < App initialCount = { 0 } /> );
// Later update - React will diff and update efficiently
setTimeout (() => {
root . render ( < App initialCount = { 5 } /> );
}, 1000 );
Cleaning Up
import { createRoot } from 'react-dom/client' ;
const root = createRoot ( document . getElementById ( 'root' ));
root . render ( < App /> );
// Clean up when done (e.g., in SPA navigation)
function cleanup () {
root . unmount ();
// Container is now empty and ready for reuse
}
// Call cleanup when navigating away or app unmounts
window . addEventListener ( 'beforeunload' , cleanup );
TypeScript
import { createRoot , type Root } from 'react-dom/client' ;
interface CreateRootOptions {
unstable_strictMode ?: boolean ;
unstable_transitionCallbacks ?: TransitionTracingCallbacks ;
identifierPrefix ?: string ;
onUncaughtError ?: (
error : unknown ,
errorInfo : { componentStack ?: string }
) => void ;
onCaughtError ?: (
error : unknown ,
errorInfo : {
componentStack ?: string ;
errorBoundary ?: React . Component ;
}
) => void ;
onRecoverableError ?: (
error : unknown ,
errorInfo : { componentStack ?: string }
) => void ;
onDefaultTransitionIndicator ?: () => void | (() => void );
}
const container = document . getElementById ( 'root' );
if ( ! container ) throw new Error ( 'Root container not found' );
const root : Root = createRoot ( container , {
onRecoverableError : ( error , errorInfo ) => {
console . error ( 'Recoverable error:' , error );
},
});
root . render (< App />);
Migration from React 17
Before (React 17)
import ReactDOM from 'react-dom' ;
ReactDOM . render (
< App /> ,
document . getElementById ( 'root' ),
() => {
console . log ( 'Rendered!' );
}
);
// Unmounting
ReactDOM . unmountComponentAtNode ( document . getElementById ( 'root' ));
After (React 18+)
import { createRoot } from 'react-dom/client' ;
import { useEffect } from 'react' ;
const root = createRoot ( document . getElementById ( 'root' ));
// Move callbacks into component
function App () {
useEffect (() => {
console . log ( 'Rendered!' );
}, []);
return < div > ... </ div > ;
}
root . render ( < App /> );
// Unmounting
root . unmount ();
Common Pitfalls
Don’t call render in render
// Bad: Causes infinite loop
function App () {
root . render ( < App /> ); // Don't do this!
return < div > App </ div > ;
}
// Good: Use state to trigger updates
function App () {
const [ count , setCount ] = useState ( 0 );
return < button onClick = { () => setCount ( count + 1 ) } > { count } </ button > ;
}
Don’t create multiple roots for same container
// Bad: Creating multiple roots for same element
const root1 = createRoot ( container );
const root2 = createRoot ( container ); // Error!
// Good: Reuse the same root
const root = createRoot ( container );
root . render ( < App1 /> );
root . render ( < App2 /> ); // This is fine - updates the same root
Don’t forget to handle errors
// Bad: Errors crash the app silently
const root = createRoot ( container );
root . render ( < App /> );
// Good: Handle errors gracefully
const root = createRoot ( container , {
onRecoverableError : ( error ) => {
logToService ( error );
},
});
root . render (
< ErrorBoundary >
< App />
</ ErrorBoundary >
);
Browser Compatibility
createRoot requires:
Modern browsers (Chrome 90+, Firefox 88+, Safari 14.1+, Edge 90+)
ES6 features : Map, Set, Promise
DOM features : Element, Document, DocumentFragment
For older browsers, include polyfills:
< script src = "https://unpkg.com/core-js-bundle@3/minified.js" ></ script >
Concurrent rendering - createRoot enables concurrent features like transitions and Suspense
Automatic batching - Multiple state updates are batched automatically
Interruptible rendering - React can pause and resume rendering work
Smooth updates - Long-running updates don’t block user interactions