Render React element inside shadow DOM in React v18
Suppose we want to render a React element (i.e., a button) after an existing element (i.e., an input box) in HTML. There are 2 ways to go about it. The first is without the shadow DOM approach, and the second is with shadow DOM. In both cases, we will use the official ReactDOMClient library and its createRoot
method.
Install ReactDOMClient library if not already:
npm i react-dom/client
Render React element without shadow DOM
Letâs render a button
element via React next to an existing input
element in HTML:
Javascript / Typescript code:
import { createRoot } from "react-dom/client";
// create root container where react element will be inserted
let container = document.createElement("div");
container.classList.add("react-container")
// create react element
const reactButton = <button>Submit</button>;
// get hold of an existing element in HTML DOM
const domElement = document.getElementById("name");
// insert root container element in HTML DOM after the existing element
domElement.after(container);
// container as react root
const root = createRoot(container);
// render react element inside the container
root.render(reactButton);
HTML DOM before rendering the react element:
<body>
<label for="name">Name:</label>
<input type="text" id="name" name="name">
</body>
HTML DOM after rendering the react element:
notice the new div with react-container
class
<body>
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<div class="react-container">
<button>Submit</button>
</div>
</body>
Note: any styles applied to the page will also be applied to the elements rendered by React, i.e., Submit button will inherit styles from the page CSS.
Render React element with shadow DOM approach
Whatâs a shadow DOM?
Itâs a separate and isolated DOM for an element. Itâs used to keep the markup structure, style, and behavior hidden and separated from other code on the page so that different parts do not clash and the code can be kept nice and clean.
Read more about shadow DOM:
Why should one use the shadow DOM approach in React?
If you render React element(s) inside a shadow DOM, the React element(s) wonât inherit any styles from the page CSS. For example, a third-party plugin that overlays some UI on top of a page would use a shadow DOM to prevent its UI from being affected by any page style.
Styling shadow DOM elements:
You can apply styling to shadow DOM elements by creating a style element or linking a css file.
To apply styling at runtime to shadow DOM element:
const shadowRoot = document.querySelector(".react-container").shadowRoot;
const button = shadowRoot.querySelector('div[role="button"');
button.style.display = "none";
Adding Tailwind support to shadow DOM:
Refer my other article: Add Tailwind styles to shadow DOM (gourav.io)
Now, Letâs render a button
element via React next to an existing input
element in HTML:
Javascript / Typescript code:
import { createRoot } from "react-dom/client";
// create root container where react element will be inserted
let container = document.createElement("div");
container.classList.add("react-container");
// attach shadow DOM to container
const shadowRoot = container.attachShadow({ mode: "open" });
// create react element
const reactButton = <button>Submit</button>;
// get hold of an existing element in HTML DOM
const domElement = document.getElementById("name");
// insert root container element in HTML DOM after the existing element
domElement.after(container);
// shadow DOM as react root
const root = createRoot(shadowRoot);
// render react element inside shadow DOM
root.render(reactButton);
HTML DOM before rendering the React element:
<body>
<label for="name">Name:</label>
<input type="text" id="name" name="name">
</body>
HTML DOM after rendering the React element:
notice the new div with react-container
class and shadow-root