Edge Rewrite
Jump to content

Shadow DOM

From Wikipedia, the free encyclopedia

Shadow DOM is a browser feature that allows websites to define self-contained HTML elements on the website. These self-contained elements, called shadow trees, allow website developers to bundle together related HTML markup and CSS styling in a manner such that the styling and code does not affect other surrounding elements. This solves a recurring problem that web developers face of unintended interference between components, where CSS rules written for one part of a page can accidentally affect unrelated parts of the website.

The shadow DOM feature originated as part of the Web Components initiative by Google alongside other proposals like Custom Elements and the HTML template element in 2013. After criticism from Apple and Mozilla on the first version of the Shadow DOM proposal, called the "v0" design, a new proposal called the "v1" design was published and was subsequently adopted by all major web browsers.

History

[edit]

Shadow DOM traces its origins to discussions between 2010 and 2011 among Google engineers and other contributors on W3C public mailing lists, where ideas for DOM encapsulation were first explored.[1] An initial "v0" specification, published as a W3C Working Draft in 2013, introduced features including multiple shadow trees per host element and a <content> element for content projection.[2] Google shipped v0 in Chrome alongside Custom Elements v0 and HTML Imports, but other browser vendors did not follow.[3]

Apple's WebKit team raised concerns about the complexity of v0 and proposed a simpler, slot-based API.[3][1] Mozilla's Firefox team aligned with the WebKit team on opposing many implementation details of the v0 API.[3][4] Disagreements between browser vendors on the shape and implementation of the API led to a "face-to-face" meeting of the W3C Web Applications Working Group at Mountain View in April 2015.[4][3][1] Discussion following the meeting resulted in the browser vendors agreeing on a "v1" design which removed multiple shadow roots per host, replaced <content> with <slot>, and shifted towards a more declarative API.[1] In 2018, W3C published a Working Group Note that the new version of the Shadow DOM API would be incorporated into various web specifications instead of being published through Working Drafts.[5] The WHATWG DOM Standard now defines shadow trees, shadow roots, and event dispatch across shadow boundaries as part of the living standard.[6]

Chrome shipped Shadow DOM v1 in version 53 (2016),[7] Safari followed later that year,[8] and Firefox added support in 2018.[9] Microsoft Edge gained support in version 79, in which it transitioned to the Chromium engine.[10][11] Chrome deprecated and removed Shadow DOM v0, Custom Elements v0, and HTML Imports in 2019.[12] A new addition to the specifications, declarative Shadow DOM has shipped in Chromium-based browsers and Safari as of 2024.[13][14]

Overview

[edit]

Shadow DOM is one of three core technologies in the Web Components suite, alongside Custom Elements and the HTML template element.[15][16] Together they enable creation of reusable, encapsulated components that combine markup, style, and behavior natively on the web. Shadow trees allow such components to render internal structure without exposing it as part of the page's main DOM.[6][17] Shadow-like mechanisms have long been used internally by browsers to implement complex built-in elements. Elements such as <video>...</video> and <details>...</details> render their controls through internal shadow trees while presenting a simple interface in the light DOM. Shadow DOM extends the same capability to developer-defined elements.[18]

The Shadow DOM consists of multiple shadow trees which are HTML DOM trees whose root is called a shadow root, attached to an element called the host element in the normal DOM tree of the page of a website. The shadow root itself is not part of the regular document DOM, it exists as a separate tree accessible only through the host element's shadowRoot property (in open mode) or not at all (in closed mode).[19] Earlier v0 drafts permitted multiple shadow trees per host, but v1 removed this in favour of simpler slot-based composition that only allows one shadow tree per host.[20] Elements outside the shadow DOM are called the light DOM.[21]

A shadow root is created by calling attachShadow() on an element, passing an options object with a mode property. A call of the form element.attachShadow({mode: 'open'}) returns a reference to the new shadow root, after which child nodes, attributes, and styles can be appended to it using standard DOM APIs.[22][23] In practice, authors typically call attachShadow() inside a custom element constructor or populate the shadow tree by cloning the contents of an <template>...</template> element.[24][25] The <template> element's content is inert until instantiated. The template element exposes a content property which exposes a DocumentFragment that can be inserted directly into the shadow tree.[25]

Features

[edit]

Encapsulation

[edit]

Styles defined inside a shadow tree are scoped to that tree and do not leak outward; selectors from the outer document do not match elements inside the shadow tree.[26] Two CSS integration points allow controlled styling across the boundary: the :host pseudo-class styles the host element from within its own shadow tree,[26] while the ::slotted() pseudo-element targets DOM elements inside slots.[27] The mode option passed to attachShadow() determines whether the shadow root is open or closed. In open mode the host's shadowRoot property returns the shadow root, allowing external scripts to traverse the tree.[28][19] In closed mode shadowRoot returns null [19][29] discouraging traversal.[22]

Content projection

[edit]

Shadow DOM defines elements serves as a placeholder inside a shadow tree into which children from the host's light DOM are placed. This is called content projection.[26] In the v1 specification this is typically done through <slot>...</slot> elements. Slots can be unnamed (the default slot) or named via a name attribute; light DOM children target a specific slot by setting a matching slot attribute.[28] Unassigned children fall through to the default slot. This mechanism replaced the <content>...</content> element from v0, which used CSS selectors to determine which light DOM nodes appeared at a given insertion point.[26] The switch to <slot>...</slot> was part of the v1 redesign agreed between browser vendors to simplify the specification and better support component subclassing.[1]

Declarative Shadow DOM

[edit]

Declarative Shadow DOM allows shadow roots to be defined directly in HTML markup without JavaScript. A <template>...</template> element with a shadowrootmode attribute placed inside a host element causes the browser to attach a shadow root during HTML parsing, moving the template's children into the shadow tree and removing the template from the DOM.[14] The feature is primarily aimed at server-side rendering of web components and environments where JavaScript may be unavailable or restricted. By constructing shadow trees during parsing rather than at runtime, it can reduce layout shifting and improve metrics such as Largest Contentful Paint.[13][30]

Use cases

[edit]

Browsers have long used internal shadow trees to implement built-in user interface elements such as form controls and media players, hiding implementation details from the page DOM. Standardized Shadow DOM extends this capability to author-defined components, enabling custom elements that expose a simple API while keeping internal markup and styles encapsulated.[18] Developers can use Shadow DOM when building design systems, reusable UI widgets, and embeddable components that must coexist with arbitrary host pages without CSS collisions.[26] Declarative Shadow DOM extends these patterns to server-rendered environments, allowing components to arrive fully structured from the server without depending on client-side JavaScript.[30]

Shadow DOM's style scoping allows authors to reuse common class names inside different components without risking conflicts with global stylesheets, and reduces dependence on deeply nested selectors.[31] Components can manage their own DOM and CSS independently, which can improve maintainability by decreasing naming collisions and unexpected cascading behavior in large applications composed of many widgets.[31][32] This has led to JavaScript frameworks adopting Shadow DOM as a component boundary. Frameworks like Polymer and it's successor Lit are built directly on top of Shadow DOM and its polyfills, use the terms local DOM for a component's shadow tree and light DOM, to describe elements managed by the framework.[33][34] Other frameworks like Angular, Vue and Svelte support using the Shadow DOM API to encapsulate styles inside their components.[35][36][37]

Limitations

[edit]

Shadow DOM v0 attracted criticism for complexity and for aspects of its encapsulation model that made subclassing difficult, which contributed to its limited adoption outside Chrome and motivated the v1 redesign.[1][3][4]

The v1 API introduces additional complexity for tooling and debugging, particularly in applications that combine Web Components with existing frameworks or rely on global CSS. Encapsulation can make it harder to inspect semantics and test the composed accessibility tree, requiring deliberate attention to WAI-ARIA roles and keyboard navigation across shadow boundaries.[21] Form participation is also not automatic — inputs inside a shadow tree do not submit their values with a containing light DOM form by default, and form validation states are not propagated across the shadow boundary.[25][18]

Shadow DOM is a tool for DOM composition and encapsulation, not a security boundary. The host element's shadowRoot property in open mode, along with various other DOM APIs, can expose shadow tree contents to scripts on the page.[38][39]

References

[edit]
  1. 1 2 3 4 5 6 Miksovsky, Jan (2019-04-08). "A history of the HTML slot element - Component Kitchen". component.kitchen. Retrieved 2026-07-08.
  2. "Shadow DOM". www.w3.org. Retrieved 2026-07-08.
  3. 1 2 3 4 5 "The state of Web Components – Mozilla Hacks - the Web developer blog". Mozilla Hacks – the Web developer blog. Retrieved 2026-07-08.
  4. 1 2 3 "Webapps/WebComponentsApril2015Meeting - W3C Wiki". www.w3.org. Retrieved 2026-07-08.
  5. "Shadow DOM being upstreamed". www.w3.org. Retrieved 2026-07-08.
  6. 1 2 "Shadow trees". WHATWG DOM Living Standard. WHATWG. Retrieved 2026-06-23.
  7. "Chrome 53 Beta: Shadow DOM". Chromium Blog. Google. Retrieved 2026-06-23.
  8. "Shadow DOM v1 - Self-Contained Web Components | Articles". web.dev. Retrieved 2026-07-08.
  9. "Firefox 63.0 beta Release Notes". Firefox. Retrieved 2026-07-08.
  10. "ShadowRoot - Web APIs | MDN". developer.mozilla.org. Retrieved 2026-07-08.
  11. "Microsoft Edge 79 to Use the Chromium Browser Engine". InfoQ. Retrieved 2026-07-08.
  12. "Intent to Deprecate and Remove: Shadow DOM v0". blink-dev. Google. Retrieved 2026-06-23.
  13. 1 2 "Declarative Shadow DOM". WebKit. Retrieved 2026-06-23.
  14. 1 2 "Declarative Shadow DOM". web.dev. Google. Retrieved 2026-06-23.
  15. "Web Components - Web APIs | MDN". MDN Web Docs. 2026-02-22. Retrieved 2026-07-08.
  16. "An Introduction to Web Components". CSS-Tricks. 2019-03-18. Retrieved 2026-07-08.
  17. "Web Components Vs. Framework Components: What's The Difference?". Smashing Magazine. 2025-03-17. Retrieved 2026-06-23.
  18. 1 2 3 Beswick, Russell (2025-07-28). "Web Components: Working With Shadow DOM". Smashing Magazine. Retrieved 2026-06-23.
  19. 1 2 3 Frisbie, Matt (2019-10-02). Professional JavaScript for Web Developers. John Wiley & Sons. pp. 797–801. ISBN 978-1-119-36657-7.
  20. Guo, Xiaojie; Huang, Yanyu; Ye, Jinhui; Yin, Sijie; Li, Min; Li, Zhaohui; Yiu, Siu-Ming; Cheng, Xiaochun (2021-08-01). "ShadowFPE: New Encrypted Web Application Solution Based on Shadow DOM". Mobile Networks and Applications. 26 (4): 1733–1746. doi:10.1007/s11036-019-01509-y. ISSN 1572-8153.
  21. 1 2 Matuzovic, Manuel (2024-06-14). Web Accessibility Cookbook. "O'Reilly Media, Inc.". pp. 301–306. ISBN 978-1-0981-4557-6.
  22. 1 2 "Using shadow DOM". MDN Web Docs. Mozilla. Retrieved 2026-06-23.
  23. Williams, Caleb (2019-03-22). "Encapsulating Style and Structure with Shadow DOM". CSS-Tricks. Retrieved 2026-06-23.
  24. Powers, Shelley (2015-01-26). JavaScript Cookbook: Programming the Web. "O'Reilly Media, Inc.". pp. 440–444. ISBN 978-1-4919-0246-2.
  25. 1 2 3 "Web Components Demystified". CSS-Tricks. Retrieved 2026-06-23.
  26. 1 2 3 4 5 Overson, Jarrod; Strimpel, Jason (2015-02-17). Developing Web Components: UI from jQuery to Polymer. "O'Reilly Media, Inc.". pp. 107–123. ISBN 978-1-4919-0569-2.
  27. "::slotted() CSS pseudo-element - CSS | MDN". MDN Web Docs. 2026-04-17. Retrieved 2026-07-08.
  28. 1 2 Flanagan, David (2020-05-14). JavaScript: The Definitive Guide: Master the World's Most-Used Programming Language. "O'Reilly Media, Inc.". p. 478. ISBN 978-1-4919-5200-9.
  29. "Element: attachShadow() method - Web APIs | MDN". MDN Web Docs. 2026-05-17. Retrieved 2026-07-08.
  30. 1 2 "Declarative Shadow DOM". DebugBear. Retrieved 2026-06-23.
  31. 1 2 "Styling in the Shadow DOM With CSS Shadow Parts". CSS-Tricks. 2020-10-28. Retrieved 2026-06-23.
  32. Lambert, Steven (2016-12-15). "Styling Web Components Using A Shared Style Sheet". Smashing Magazine. Retrieved 2026-06-23.
  33. Savage, Taylor (November 2015). "Componentizing the Web: We may be on the cusp of a new revolution in web development". Queue. 13 (8): 60–79. doi:10.1145/2838344.2844732. ISSN 1542-7730.
  34. "Working with Shadow DOM – Lit". lit.dev. Retrieved 2026-07-03.
  35. Team, Angular. "ViewEncapsulation • Angular". angular.dev. Retrieved 2026-07-08.
  36. "Vue and Web Components". vuejs.org. Retrieved 2026-07-08.
  37. "Custom elements • Svelte Docs". svelte.dev. Retrieved 2026-07-08.
  38. "ShadowBreakers". GitHub. Retrieved 2026-06-23.
  39. "Declarative Shadow DOM Security and Privacy Self-Review". GitHub. Retrieved 2026-06-23.