Side by side
Shadow DOMvsVirtual DOM
What is the difference between the shadow DOM and the virtual DOM?
Updated 3 min read7 differences
In short
They solve different problems: the shadow DOM isolates a component's markup and styles, while the virtual DOM helps libraries re-render the page efficiently.
Shadow DOM
The shadow DOM is a browser feature that attaches a hidden, separate DOM tree to an element, so its markup and styles stay isolated from the rest of the page.
Read the page on Shadow DOMVirtual DOM
The virtual DOM is a lightweight copy of a page's element tree kept in JavaScript memory, which UI libraries compare to update the real DOM efficiently.
Read the page on Virtual DOMShadow DOM and Virtual DOM compared
| Aspect | Shadow DOM | Virtual DOM |
|---|---|---|
| Problem it solves | Encapsulation: keeps a component's markup and styles isolated | Efficient re-rendering: works out the fewest DOM changes |
| What it is | A browser standard, part of the real DOM | A technique used by libraries, held in JavaScript memory |
| Drawn on screen | Yes; the shadow tree is rendered like the rest of the page | No; only the real DOM changes it produces are drawn |
| Provided by | The browser, through attachShadow() or declarative HTML | A library such as React, Preact or Vue |
| Effect on CSS | Page styles stay out and component styles stay in | None; styles apply to the resulting real DOM as usual |
| Effect on update speed | None; it scopes markup and styles, not updates | Keeps updates reasonably fast while the code stays declarative |
| Typical users | Web Components, Lit, browser controls such as <video> | React, Preact and Vue apps |
The difference, explained
Despite the similar names, the two solve different problems. The shadow DOM is a browser standard for encapsulation: attachShadow() gives an element its own hidden DOM tree, whose styles don't leak out and into which the page's styles don't leak. The virtual DOM is a technique for efficient re-rendering: libraries such as React and Vue keep a copy of the UI as plain JavaScript objects, compare a new copy with the old one and apply only the differences to the real DOM.
They also live in different places. A shadow tree is part of the real DOM that the browser draws; it keeps a component's insides private, and it is one of the standards behind Web Components, along with custom elements and <template>. The virtual DOM exists only in a library's memory and is never drawn itself; it lets developers describe what the UI should look like for a given state and leave the DOM operations to the library.
Because they answer different questions, they can work together. A React or Vue component can render into a shadow root to get style isolation, and Vue's defineCustomElement turns a component into a custom element that uses the shadow DOM. Each also appears without the other: Lit, a popular Web Components library, uses the shadow DOM but no virtual DOM, and Svelte and Solid skip the virtual DOM entirely.
A common misconception is that the shadow DOM is a faster, built-in version of the virtual DOM. It does nothing to speed up updates, and the virtual DOM does nothing to isolate styles. Another is that a closed shadow root is a security boundary; it only hides the tree from outside scripts, which is encapsulation, not protection.
Which one should you use?
Choose Shadow DOM when…
- A widget must look the same on any page, whatever CSS that page has.
- You build Web Components to share across frameworks or plain HTML pages.
- Page styles and scripts must not reach into a component by accident.
Choose Virtual DOM when…
- You want to describe the UI declaratively and let a library such as React or Vue update the DOM.
- The interface changes often in response to state, such as lists, forms and filters.
- You need efficient updates of a large, dynamic tree, not style isolation.
A price tag each way: isolated styles, or a diffed update
// Shadow DOM: the browser isolates this element's markup and styles
class PriceTag extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" }).innerHTML =
"<style>span { color: green; }</style><span><slot></slot></span>";
}
}
customElements.define("price-tag", PriceTag);
// <price-tag>$12</price-tag>: page CSS can't restyle the span inside,
// and the span rule styles nothing outside it// Virtual DOM: React compares the new tree with the old one
function PriceTag({ amount }) {
return <span className="price">{amount}</span>;
}
// When amount changes from "$12" to "$15", React builds a new virtual tree,
// diffs it and updates only the text node; styles are still global CSS
root.render(<PriceTag amount="$15" />);Readers ask
Is the shadow DOM the browser's version of the virtual DOM?
No. The shadow DOM isolates part of the real DOM for encapsulation and does nothing to make updates faster. The virtual DOM is a library technique for working out which DOM changes to make.
Does React use the shadow DOM?
Not by default. React renders into the normal DOM using a virtual DOM, but it can render into a shadow root you create, and since React 19 it fully supports custom elements, including ones that use the shadow DOM.
Which one should I learn first?
Learn the one your work uses. App developers using React or Vue meet the virtual DOM every day, while people building design systems or embeddable widgets meet the shadow DOM; both make more sense once you know the real DOM well.