Skip to main content

Shadow DOM vs regular DOM

The regular (light) DOM is the document's main tree, while Shadow DOM is a nested, isolated DOM tree inside an element that has its own styles, markup and behaviour and does not affect the outer document. In plain words: the light DOM is the "surface of the site", and Shadow DOM is the "insides of a component", hidden from the rest of the world.

Theory

TL;DR

  • The light DOM is everything you write in HTML and see in DevTools; Shadow DOM lives inside a host element.
  • The light DOM obeys global CSS, Shadow DOM is isolated from it.
  • You reach the light DOM with document.querySelector(), and the shadow tree only with shadowRoot.querySelector().
  • Slots (<slot>) exist only in Shadow DOM: they render light DOM content inside the component.
  • Shadow DOM is what you use for component encapsulation, style isolation and self-contained elements such as <video>.

Quick example

javascript
const card = document.querySelector('.card'); const shadow = card.attachShadow({ mode: 'open' }); shadow.innerHTML = ` <style> button { color: blue; } </style> <button>Inner</button> `;

Now card has an inner Shadow DOM that is isolated from outside styles, is not part of the ordinary document tree (and with mode: 'closed' cannot be reached from outside at all), and manages its own elements. The global rule button { color: red; } will not repaint this button, because it lives in its own shadow tree.

Definitions: light DOM and Shadow DOM

TypeWhat it is
Regular (light) DOMThe main tree of the HTML document: everything you write in HTML and see in DevTools.
Shadow DOMA nested, isolated DOM tree inside an element (a component) that has its own styles, markup and behaviour and does not affect the outer document.

For comparison, an ordinary card in the light DOM:

html
<div class="card"> <h2>Title</h2> <button>Click</button> </div>

Everything here is visible in the main DOM, the button can be grabbed with document.querySelector('button'), and global styles apply to all of it: button { color: red; } makes the button red.

Key differences

CharacteristicRegular DOM (light)Shadow DOM
Where it livesIn the document treeInside a host element
Visible in DevToolsYesYes, but hidden by default, enabled by "Show user agent shadow DOM"
StylingObeys global CSSIsolated from global styles
IsolationNone, styles and JS affect each otherFull, an isolated context
Used inAny HTMLWeb Components, built-in elements (<video>, <input> and others)
InteractionDirectly via document.querySelector()Only via shadowRoot.querySelector()
SlotsNoYes (<slot> projects content from the light DOM)

Structure and slots

html
<body> <my-card> <!-- the host element --> #shadow-root (shadow DOM) <style>...</style> <button>Click</button> #light-dom (regular DOM) <p slot="content">...</p> </my-card> </body>

Here my-card is the host element, #shadow-root is its inner Shadow DOM, and outside elements can pass content in through <slot>. Note that the slotted <p> physically stays in the light DOM and is still subject to global styles, it is merely rendered at a chosen place in the shadow tree.

Access from code

From the light DOM you only see the component's "shell":

javascript
console.log(document.querySelector('my-card').innerHTML); // Empty, because the insides are hidden in the Shadow DOM

The shadow tree is reachable only through a reference to its root:

javascript
const shadow = document.querySelector('my-card').shadowRoot; console.log(shadow.querySelector('button')); // <button>Click</button>

If the tree was created with mode: 'closed', shadowRoot is null and the inner nodes cannot be reached from outside at all.

Where you actually need it

Reach for Shadow DOM when you need to:

  • encapsulate a component (web components, UI libraries);
  • isolate styles so global CSS cannot break the look;
  • build a reusable element, for example <custom-slider>;
  • make a component self-contained, like <video> or <select>.

A real-life example. In the regular DOM you redefined button styles across the whole site, and suddenly the buttons inside a component "broke", because the styles are global. With Shadow DOM, a component with its own styles is isolated, and no global button { color: red } touches it.

CriterionRegular DOMShadow DOM
VisibilityAvailable to all codeHidden and isolated
StylesSharedIsolated
UsageEveryday HTMLComponents and built-in elements
InteractionThrough documentThrough shadowRoot
SafetyNo isolationEncapsulated

Common mistakes

  • Looking for a component's inner nodes with document.querySelector(): the selector does not cross the shadow boundary.
  • Assuming global CSS will "just work" inside a component: styles get in through the component's own <style>, CSS custom properties or ::part.
  • Confusing a slot with moving the node: slotted content stays in the light DOM, it is not copied into the shadow tree.
  • Treating closed as data protection: it only makes access harder, it provides no real security.
  • Forgetting about events: to make an event from the shadow tree bubble out you create it with composed: true, and from the outside its target will be the host.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.