Shadow DOM

Shadow DOM is a standard that encapsulates the internal document object model (DOM) structure of a web component. Encapsulating the DOM gives developers the ability to share a component and protect the component from being manipulated by arbitrary HTML, CSS, and JavaScript. The internal DOM structure is called the shadow tree. The shadow tree affects how you work with CSS, events, and the DOM.

Since not all browsers support Shadow DOM, LWC uses a synthetic shadow polyfill for Lightning Experience and Experience Cloud. A polyfill is code that allows a feature to work in a web browser.

Explaining the Shadow DOM

If you use LWC outside of Lightning Experience or Experience Cloud, such as in Lightning Out, LWC renders in native shadow. See Compile-time Differences on the Salesforce Platform.

To understand the shadow tree, let’s look at some markup. This markup contains two Lightning web components: c-todo-app and c-todo-item. The #shadow-root document fragment defines the boundary between the DOM and the shadow tree. Elements below the shadow root are in the shadow tree.

Let’s look at how to work with the shadow tree in each of these areas.

CSS

CSS styles defined in a parent component don’t leak into a child. In our example, a p style defined in the todoApp.css stylesheet doesn’t style the p element in the c-todo-item component, because the styles don’t reach into the shadow tree. See CSS.

Events

To prevent exposing a component’s internal details, if an event bubbles up and crosses the shadow boundary, some property values change to match the scope of the listener. See Event Retargeting.

Access Elements

Elements in a shadow tree aren’t accessible via traditional DOM querying methods. Code can’t use document or document.body to access the shadow tree of a Lightning web component. For example, code can’t call document.querySelector() to select nodes in a Lightning web component’s shadow tree. To access its own shadow tree, a Lightning web component calls this.template.querySelector(). See Access Elements the Component Owns.

Access Slots

A slot is a placeholder for markup that a parent component passes into a component’s body. DOM elements that are passed to a component via slots aren’t owned by the component and aren’t in the component’s shadow tree. To access DOM elements passed via slots, call this.querySelector(). The component doesn't own these elements, so you don’t use template. See Pass Markup into Slots.

Test Components

See DOM Inspection Tests Are Subject to Change.

Watch Salesforce Developer Evangelist Alba Rivas explain the Shadow DOM.

Don’t use these DOM APIs to reach into a component’s shadow tree in orgs that use Lightning Locker. If you’re using a third-party JavaScript library that uses these DOM APIs to reach into a component’s shadow tree, work with the library author to file and fix issues.

  • Document.prototype.getElementById
  • Document.prototype.querySelector
  • Document.prototype.querySelectorAll
  • Document.prototype.getElementsByClassName
  • Document.prototype.getElementsByTagName
  • Document.prototype.getElementsByTagNameNS
  • Document.prototype.getElementsByName
  • document.body.querySelector
  • document.body.querySelectorAll
  • document.body.getElementsByClassName
  • document.body.getElementsByTagName
  • document.body.getElementsByTagNameNS

Lightning Locker prevents you from breaking shadow DOM encapsulation between Lightning web components by blocking these APIs. However, in Aura components version 39.0 and earlier, Lightning Locker is disabled at the component level, so an Aura component could have code that fails.

These APIs aren’t restricted by Lightning Web Security (LWS). LWS prevents you from breaking shadow DOM encapsulation by enforcing a value of closed on the ShadowRoot's mode property for all components.

The Shadow DOM polyfill includes a patch to the MutationObserver interface. If you use MutationObserver to watch changes in a DOM tree, disconnect it or you will create a memory leak. Note that a component can observe mutations only in its own template. It can't observe mutations within the shadow tree of other custom elements.