Volume I index
The First React Screen

createRoot and the Root Container

Ishtmeet Singh @ishtms/July 14, 2026/9 min read
#react#react-dom#createroot#dom#root

The connection between the HTML document and App.jsx lives inside main.jsx. Open that file and name every value in the connection.

A client React application needs an existing DOM node. React DOM attaches a root to that node. The root receives React content and manages the container's descendants.

text
HTML creates a DOM container
main.jsx finds that container
createRoot attaches a React root
root.render supplies React content

The four lines describe separate actions. Keeping them separate prevents the root container, the React root, and the application component from becoming one vague idea.

Find the Container in HTML

Open index.html at the project root. Its body contains an element close to this one.

html
<div id="root"></div>

The browser parses the element before the module script runs. It creates a HTMLDivElement and stores root as the element's id.

The word root in that ID has no built-in React behavior. Another ID works too.

html
<main id="application"></main>

React only needs a DOM node. The name becomes meaningful because main.jsx searches for the same ID.

The default Vite document also loads the client entry.

html
<script type="module" src="/src/main.jsx"></script>

The browser encounters the module script, requests main.jsx through Vite, and evaluates its imports. By that time, the earlier div has been parsed and can be found through document.

Read main.jsx One Value at a Time

The generated entry is close to this form.

jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
import "./index.css";

The imports make four names or effects available. StrictMode is a React development checking component. createRoot is the React DOM client function. App is the application component. The CSS import asks Vite to include document styles.

The entry then finds the DOM node.

jsx
const container = document.getElementById("root");

document comes from the browser. getElementById returns the element whose ID matches, or null when no such element exists.

The next line creates a React root.

jsx
const root = createRoot(container);

Finally, the root receives React content.

jsx
root.render(<App />);

The variable names are local choices. These names communicate the two values clearly.

text
container holds a DOM node
root holds a React DOM root object

Check the Container Before Calling React DOM

JavaScript allows container to be null. createRoot requires a DOM element.

The HTML and JavaScript can disagree after an ID edit.

html
<div id="application"></div>
jsx
const container = document.getElementById("root");

No element has the requested ID, so the lookup returns null. Passing that value to createRoot produces an error instead of a screen.

Make the requirement explicit.

jsx
const container = document.getElementById("root");

if (!container) {
  throw new Error('Missing the DOM element with id="root"');
}

Only create the root after the check.

jsx
const root = createRoot(container);
root.render(<App />);

The error message names the exact missing requirement. A generic message such as Failed to start would force the reader to search for the condition.

In TypeScript, this check also narrows the value from HTMLElement | null to HTMLElement for the remaining code.

createRoot Manages a Container

createRoot(container) tells React DOM that React will manage content inside this container. It returns a root with methods including render and unmount.

The function does not return the div.

jsx
const root = createRoot(container);

root is not a DOM element, so browser element methods cannot be called on it.

jsx
root.appendChild(node);

The DOM container still exists in container. React content is supplied through root.render.

Create one root for a container and retain it. Calling createRoot again on the same node is not a way to update the application.

jsx
const firstRoot = createRoot(container);
const secondRoot = createRoot(container);

React reports a warning because a root is already attached to the container. Use the existing root for later renders.

jsx
root.render(<App />);

The First root.render Call

root.render(<App />) requests React to display the supplied React node inside the root.

jsx
root.render(<App />);

<App /> produces a React element description. React calls the component while calculating the tree, and React DOM commits the required DOM nodes.

If the root container already has child HTML, the first completed client render replaces the container's existing children with React's result.

html
<div id="root">
  <p>Loading application</p>
</div>

The paragraph can appear before JavaScript loads. After the first React render completes, React manages the children and the paragraph remains only if React output includes equivalent content.

This behavior is different from hydration. hydrateRoot attaches React to server-produced HTML that already matches the React output.

Render StrictMode Around the App

The Vite template normally supplies this root content.

jsx
root.render(
  <StrictMode>
    <App />
  </StrictMode>,
);

StrictMode enables additional development checks for the React tree inside it. These checks can run selected rendering work again and can repeat Effect setup in development. They expose component code that changes external data during rendering or omits cleanup.

Strict Mode has no visible DOM element. The browser DOM begins with whatever App returns.

It also does not make the production bundle render everything twice. The additional checks described here run in development.

Leave it in place. When a later log appears more than once, investigate whether the log sits in render-time code before removing the check.

Content Outside the Container Remains Outside the Root

React manages descendants inside its container. The surrounding document can remain ordinary HTML.

html
<body>
  <header>ReactBook</header>
  <div id="root"></div>
  <footer>Copyright 2026</footer>
</body>

Rendering App does not remove the header or footer because neither is a child of the root container.

The root can also render portals into another DOM node, but ordinary component output stays within the root's managed tree.

Application components should determine descendants of the root. A separate script should not replace those descendants after React has started.

More Than One Root

An existing page can contain two independent React regions.

html
<div id="reaction-controls"></div>
<div id="reading-progress"></div>

Each region receives its own root.

jsx
const reactionsRoot = createRoot(reactionsContainer);
const progressRoot = createRoot(progressContainer);

The roots can render different component trees.

jsx
reactionsRoot.render(<ReactionControls />);
progressRoot.render(<ReadingProgress />);

This works for independent additions to an existing page. A full React application normally prefers one root so components can share state, context, routing, and error handling inside one tree.

Two roots do not create a parent-child React relationship merely because their DOM containers are near each other.

Update the Existing Root

A root can receive another render call.

jsx
root.render(<App mode="reading" />);

Later, the same root can receive another element.

jsx
root.render(<App mode="review" />);

React compares the next tree with the current tree and preserves compatible component state. Application code rarely calls the top-level root.render for each interaction. State setters inside components request normal updates. The root remains the application entry point.

Changing the top-level component type changes the rendered tree.

jsx
root.render(<AdminApp />);

React may remove the previous App instance and create AdminApp. A change in component type can end one component instance and start another.

Unmount the Root

Some pages remove a React region while the surrounding document remains active. Call unmount on the root before another system removes the container.

jsx
root.unmount();

React removes the rendered DOM content and cleans up component resources in that tree, including subscriptions established through Effects.

The root cannot render again after it has been unmounted.

jsx
root.unmount();
root.render(<App />);

React reports an error because that root has ended. Create another root if the same DOM container later begins a new React tree.

Do not unmount during ordinary navigation inside a React application. Routers and conditional rendering change components while the application root remains active.

Keep Root Code Small

The client entry should make its startup work visible.

jsx
const container = document.getElementById("root");

if (!container) {
  throw new Error('Missing the DOM element with id="root"');
}

createRoot(container).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Using the returned root inline is valid when no later code needs unmount or another explicit render call. A named root is also valid and exposes the object for inspection.

Do not place application sections inside main.jsx merely because it runs first. App defines the application UI. The entry performs browser startup and root creation.

Inspect the Result in Two Panels

Open the browser Elements panel. Under div#root, you should see the h1 returned by App.

text
div#root
  h1
    My first React screen

The div came from index.html. The h1 came from the React render.

With React DevTools installed, the Components panel shows StrictMode behavior and App in the React tree. It does not replace the Elements panel; the two panels describe different structures.

Root Failure Check

Temporarily change the HTML ID to application while leaving the JavaScript lookup as root. Save and reload.

The custom error should report the missing element. Restore the ID and confirm the heading returns.

This controlled failure proves that the JavaScript does not locate the container by filename, tag order, or React convention. It uses the exact DOM lookup written in the entry.

Lesson Check

Point to four distinct values or sources in the running project.

text
index.html creates the DOM container
document.getElementById returns that container
createRoot returns the React root
root.render receives the React application element

Source Notes