If you’re building Lightning Web Components (LWC), you’ve probably heard of the Shadow DOM. But unless you’re coming from a hardcore web standards background, it feels like a little abstract. Let’s break it down from the basics and explain how Shadow DOM fits into the picture — especially in the context of LWC.
🧱 First, What is the DOM?
DOM stands for Document Object Model. It’s the browser’s way of representing the HTML page as a tree of objects — every element (like a div, p, or button) becomes a node in this tree.
It is simply an tree structure representation of the web page. Think of the DOM as the live structure of the web page. JavaScript uses this to read and change what’s displayed.
<html>
<body>
<div id="app">
<h1>Hello World</h1>
</div>
</body>
</html>
🌑 So What is Shadow DOM Then?
The Shadow DOM is like a private branch of the DOM tree that’s hidden away from the main DOM. It was introduced as part of the Web Components standard to solve a big problem: encapsulation.
In LWC, when we add one child component to parent component, the DOM of the child component in the parent component becomes SHADOW DOM.
Imagine you’re building a custom my-button component. Without Shadow DOM, the styles you define for that button could clash with styles on the parent— especially in big apps. Shadow DOM gives your component its own DOM subtree which is isolated. It has its own styles and own markup and it is shielded from the outside world.
Benefits:
- Shadow DOM doesn’t allow to query the DOM elements from the parent
- Shadow DOM doesn’t override the CSS
Differences at a glance:
| Feature | DOM (Light DOM) | Shadow DOM |
| Global Access | Yes | No |
| Style Leakage | Yes | No |
| Encapsulation | No | Yes |
| Used By Default in LWC | No | Yes |
🧩 How Shadow DOM Comes Into Play in LWC
In Lightning Web Components, Shadow DOM is the default rendering mode. That means every time you create a component in LWC, it gets its own Shadow DOM automatically.
You don’t need to manually create a shadow root — Salesforce handles that for you under the hood.
<!-- childComponent.html -->
<template>
<p>Hello from LWC!</p>
</template>
This looks like regular HTML, but when we add it inside a parent component, LWC renders this inside a Shadow Root. The result? The component is isolated — CSS only affects this component, and outside styles can’t interfere with it either.
🎨 Styling in the Shadow DOM
Because of encapsulation, global CSS styles won’t affect elements inside the LWC component.
Instead, we can style components with their own .css file inside the component folder:
/* childComponent.css */
p {
color: red;
}
This only applies to the <p> inside the child component — nowhere else. That’s the magic of Shadow DOM: scoped styles without needing CSS modules or naming hacks.
🌓 What if You Don’t Want Encapsulation? (Light DOM)
There are times when we might want the component to participate in the global DOM — for example, when integrating with third-party libraries, or needing global CSS to apply.
That’s where Light DOM comes in, and LWC now supports it with a simple line:
export default class ChildComponent extends LightningElement {
static renderMode = 'light';
}
Even, in standard HTML and JavaScript, we can manually create a Shadow DOM by calling attachShadow() on an element. This gives that element its own encapsulated DOM tree.
element.attachShadow({ mode: 'open' });
Then, you can add HTML and styles into the shadow root. This is the same concept that LWC uses under the hood — but LWC handles it for you automatically.
Understanding the difference between the regular DOM and Shadow DOM helps you write cleaner code. It also allows to debug faster.

Leave a comment