Open vs Closed Shadow DOM in JavaScript

Shadow DOM allows developers to encapsulate CSS and HTML structure within web components, preventing styles and DOM queries from leaking into or out of the component. When creating a shadow root using element.attachShadow({ mode: '...' }), JavaScript requires you to specify the encapsulation mode as either open or closed. This article explores the functional differences, access mechanisms, and common use cases for both open and closed Shadow DOM modes.

Understanding Shadow DOM Modes

The mode of a Shadow DOM root controls how accessible the internal DOM tree is to JavaScript running in the outer document. It is defined at initialization:

// Open mode
const openShadow = element.attachShadow({ mode: 'open' });

// Closed mode
const closedShadow = element.attachShadow({ mode: 'closed' });

Open Mode Shadow DOM

When a shadow root is set to mode: 'open', the component’s internal DOM remains accessible from external JavaScript via the host element’s .shadowRoot property.

Example:

const customElem = document.querySelector('my-open-element');
console.log(customElem.shadowRoot); // Returns the ShadowRoot object
const internalBtn = customElem.shadowRoot.querySelector('button'); // Accessible

Closed Mode Shadow DOM

When a shadow root is set to mode: 'closed', external JavaScript cannot access the shadow root through the host element.

Example:

const customElem = document.querySelector('my-closed-element');
console.log(customElem.shadowRoot); // Returns null

Is Closed Mode a Security Boundary?

Closed mode is not a security feature. It is designed to provide encapsulation and prevent accidental interference, not malicious access. External code can still bypass closed mode by monkey-patching Element.prototype.attachShadow before the component initializes:

const originalAttachShadow = Element.prototype.attachShadow;
Element.prototype.attachShadow = function(init) {
  const shadowRoot = originalAttachShadow.call(this, init);
  console.log('Intercepted closed root:', shadowRoot);
  return shadowRoot;
};

Summary of Differences

Feature Open Mode Closed Mode
element.shadowRoot Returns ShadowRoot Returns null
External DOM Access Allowed Disallowed (without internal references)
Testability High Requires specific architectural workarounds
Style Encapsulation Complete (CSS stays isolated) Complete (CSS stays isolated)
Security Not a security boundary Not a security boundary

Best Practices

In almost all standard web development scenarios, mode: 'open' is recommended. It delivers the style and markup encapsulation benefits of Web Components while maintaining the transparency required for testing, accessibility tools, and debugging. mode: 'closed' should generally be reserved for native browser controls (such as <video> or <input>) or specialized widgets requiring strict internal state isolation.