Objective: Ensure that styles from specific components don't bleed into the main DOM of the webpage, preserving the page's existing styles.
The code snippet below demonstrates how to initialize a React widget. This widget imports a stylesheet from an external library. The key here is that the styles from this external library don't affect the main page, thanks to the use of the Shadow DOM.
import React from "react";
import ReactDOM from "react-dom";
import ExternalStyles from "@library/styles/stylesheet.css";
let root;
function init({ context, rootElement }) {
// Initiate the Shadow DOM
const shadow = rootElement.attachShadow({ mode: "open" });
const renderInShadow = document.createElement("div");
const styles = document.createElement("style");
root = renderInShadow;
styles.textContent = ExternalStyles.toString();
rootElement.setAttribute("data-shadowed", "style-isolated-shadowed");
shadow.appendChild(renderInShadow);
shadow.appendChild(styles);
// ... other initialization procedures ...
// Signal that React has finished its rendering
ReactDOM.render(
// ... React components ...
renderInShadow,
() => {
const event = new CustomEvent("ReactDOMReady", {
detail: { message: "React component is now rendered!" },
bubbles: true,
cancelable: false,
});
window.dispatchEvent(event);
}
);
}
function dispose() {
if (!root) {
return;
}
ReactDOM.unmountComponentAtNode(root);
}At the Webpack level, we'll design a custom loader. By leveraging the capabilities of the style-loader, we can ensure that styles are injected directly into the previously established Shadow DOM.
const shadowDOMLoader = {
loader: "style-loader",
options: {
insert: function (linkTag) {
window.addEventListener("ReactDOMReady", (evt) => {
const parent = document.querySelector('[data-shadowed="style-isolated-shadowed"]').shadowRoot;
parent.appendChild(linkTag);
});
},
},
};
// Webpack configuration details
{
test: /\.css$/i,
exclude: /stylesheet\.css$/i,
use: [shadowDOMLoader, "css-loader", /* other loaders... */],
}Key Takeaways:
- The initialization procedure crafts a Shadow DOM on the specified root element, ensuring the widget's content is safely encapsulated.
- Through a tailored Webpack loader (
shadowDOMLoader), styles are strategically placed inside the Shadow DOM rather than the main DOM. This insertion is prompted by the firing of theReactDOMReadyevent.