Published on Jan 22 2023
Last updated on Apr 06 2023
Shadow DOM is a powerful feature in web development that allows you to create custom HTML elements with their own styles and behavior. Today, we're going to talk about one of the most exciting and mysterious topics in web development: Shadow DOM. What is it, why do we need it, and how does it work so you can start experimenting with Shadow DOM in your web development projects, and take your code to the next level!
Shadow DOM stands for Shadow Document Object Model. It's a way to encapsulate the presentation and behavior of a web component, making it more modular and reusable. In simpler terms, it allows you to create custom HTML elements with their own styles and behavior, without interfering with the styles and behavior of the rest of the page.
When you create a web component with Shadow DOM, you define a "shadow tree" inside the component. This shadow tree is a separate DOM tree that's hidden from the rest of the page's DOM tree. The shadow tree can have its own HTML elements, styles, and behavior, which won't affect the rest of the page.
To create a web component with Shadow DOM, you can use the shadowRoot
property of the element. For example, let's say we want to create a custom button element with a specific style and behavior. We can create a new element using the createElement
method, and then set its shadowRoot
property to a new ShadowRoot
object. Here's some code to illustrate:
shadowDomExample.js
1class MyButton extends HTMLElement {2constructor() {3super();4const shadowRoot = this.attachShadow({ mode: "open" });5const button = document.createElement("button");6button.innerHTML = "Click me!";7button.onclick = () => {8alert("You clicked the button!");9};10const style = document.createElement("style");11style.textContent = `12button {13background-color: blue;14color: white;15border-radius: 5px;16padding: 10px;17}18`;19shadowRoot.appendChild(style);20shadowRoot.appendChild(button);21}22}2324customElements.define("my-button", MyButton);
In this example, we're creating a new element called my-button
that extends the "HTMLElement" class. Inside the constructor, we're creating a new "ShadowRoot" object using the attachShadow
method, and setting its mode
property to open
to allow external styles to be applied. Then, we're creating a new "button" element with the text "Click me!" and an "onclick" event that shows an alert when clicked. Finally, we're creating a new "style" element with some CSS styles for the button, and appending everything to the shadow tree.
Now, we can use our new custom button element in our HTML like this:
index.html
1<my-button></my-button>
When the page is loaded, the browser will create a new instance of our custom button element and replace the my-button
tag with the contents of the shadow tree. The result is a custom button with its own styles and behavior, encapsulated from the rest of the page.
Shadow DOM also allows you to style the contents of the shadow tree using CSS variables and CSS custom properties. This can be really useful for creating themes or allowing users to customize the appearance of your web components.
To use CSS variables and custom properties in Shadow DOM, you can define them in the shadow tree's "style" element and use them in your CSS selectors. For example, let's say we want to create a custom button element that can be styled with different colors. We can define a CSS variable for the background color and use it in our button's styles, like this:
shadowDomExample.js
1class MyButton extends HTMLElement {2constructor() {3super();4const shadowRoot = this.attachShadow({ mode: "open" });5const button = document.createElement("button");6button.innerHTML = "Click me!";7button.onclick = () => {8alert("You clicked the button!");9};10const style = document.createElement("style");11style.textContent = `12button {13background-color: var(--my-button-background-color, blue);14color: white;15border-radius: 5px;16padding: 10px;17}18`;19shadowRoot.appendChild(style);20shadowRoot.appendChild(button);21}22}2324customElements.define("my-button", MyButton);
In this example, we're defining a CSS variable called --my-button-background-color
with a default value of blue
. Then, in our button's styles, we're using the var()
function to set the background color to the value of the CSS variable. This allows us to easily change the button's background color by setting the value of the CSS variable in the shadow tree.
To set the value of the CSS variable from outside the shadow tree, we can use the setProperty
method of the shadow root's "style" element, like this:
shadowDomExample.js
1const myButton = document.querySelector("my-button");2myButton.shadowRoot.querySelector("style").setProperty(3"--my-button-background-color",4"red"5);
In this example, we're selecting the "my-button" element and getting its shadow root's "style" element. Then, we're using the "setProperty" method to set the value of the "--my-button-background-color" variable to "red." This will update the button's background color to red, without affecting the rest of the page.
Shadow DOM is a powerful tool for creating modular and reusable web components with their own styles and behavior. It allows you to encapsulate your components from the rest of the page, preventing conflicts and making it easier to maintain your code. With CSS variables and custom properties, you can also create highly customizable components that can be styled to fit any design. So, go ahead and start experimenting with Shadow DOM in your web development projects, and see how it can improve your code!
Written by Alissa Nguyen
FollowAlissa Nguyen is a software engineer with main focus is on building better software with latest technologies and frameworks such as Remix, React, and TailwindCSS. She is currently working on some side projects, exploring her hobbies, and living with her two kitties.
Learn more about me
If you found this article helpful.
You will love these ones as well.
Built and designed by Alissa Nguyen a.k.a Tam Nguyen.
Copyright © 2025 All Rights Reserved.