Element: attachShadow() method
Baseline
Widely available
*
This feature is well established and works across many devices and browser versions. It’s been available across browsers since January 2020.
* Some parts of this feature may have varying levels of support.
The attachShadow() method of the Element interface attaches a shadow DOM tree to the specified element and returns a reference to its ShadowRoot.
Syntax
attachShadow(options)
Parameters
options-
An object which contains the following fields:
mode-
A string specifying the encapsulation mode for the shadow DOM tree. This can be one of:
open-
Elements inside the shadow root are accessible from JavaScript via the element's
shadowRootproperty. closed-
Elements inside the shadow root cannot be accessed from JavaScript via the
shadowRootproperty, which is set tonull.
clonableOptional-
A boolean that specifies whether the shadow root is clonable: when set to
true, the shadow host cloned withNode.cloneNode()orDocument.importNode()will include shadow root in the copy. Its default value isfalse. customElementRegistryOptional-
A
CustomElementRegistrythat will be used as the scoped custom element registry of the attached shadow root. Ifnullorundefined, the shadow root will use the global registry referenced byWindow.customElements. delegatesFocusOptional-
A boolean that, when set to
true, specifies behavior that mitigates custom element issues around focusability. When a non-focusable part of the shadow DOM is clicked, the first focusable part is given focus, and the shadow host is given any available:focusstyling. Its default value isfalse. referenceTargetOptional-
A string value that indicates the effective target of any element reference made against the shadow host from outside the host element. The value should be the ID of an element inside the shadow DOM. If set, target references to the host element from outside the shadow DOM will cause the referenced target element to become the effective target of the reference to the host element.
serializableOptional-
A boolean that, when set to
true, indicates that the shadow root is serializable. If set, the shadow root may be serialized by calling theElement.getHTML()orShadowRoot.getHTML()methods with theoptions.serializableShadowRootsparameter settrue. Its default value isfalse. slotAssignmentOptional-
A string specifying the slot assignment mode for the shadow DOM tree. This can be one of:
named-
Elements are automatically assigned to
<slot>elements within this shadow root. Any top-level children of the host with aslotattribute which matches thenameattribute of a<slot>within this shadow root will be assigned to that slot. Any top-level children of the host with noslotattribute will be assigned to the first<slot>with nonameattribute (the "default slot"), if one is present. This is the default value. manual-
Elements are manually assigned to particular slot elements using
HTMLSlotElement.assign(). No automatic assignment takes place.
Return value
Returns a ShadowRoot object.
Exceptions
NotSupportedErrorDOMException-
This error may be thrown when you try to attach a shadow root to an element:
- outside the HTML namespace or that can't have a shadow attached to it.
- where the element definition static property
disabledFeatureshas been given a value of"shadow". - that already has a shadow root that was not created declaratively.
- that has a declarative shadow root but the specified
modedoes not match the existing mode. - while passing a
customElementRegistryvalue that isn'tnullor a locally scoped registry (that you created usingnew CustomElementRegistry()). The error would be thrown if you passed the global registry.
Description
The Element.attachShadow() method attaches a shadow DOM tree to the specified element and returns a reference to its ShadowRoot.
This is the programmatic mechanism to create a ShadowRoot, which is the root node of a Shadow DOM attached to a host element (it is also possible to create a ShadowRoot declaratively using the shadowrootmode attribute of the <template> element).
It is used for creating custom elements.
Elements you can attach a shadow to
Note that you can't attach a shadow root to every type of element.
There are some that can't have a shadow DOM for security reasons (for example <a>).
The following is a list of elements you can attach a shadow root to:
Calling this method on an element that is already a shadow host
The method may be called on an element that already has a declarative shadow root, provided the specified mode mode matches the existing mode.
In this case the ShadowRoot that was already present will be cleared and returned.
This allows for cases where, for example, server-side rendering has already declaratively created a shadow root, and then client-side code attempts to attach the root again.
Otherwise calling attachShadow() on an element that already has a shadow root will throw an exception.
Open and closed shadow roots
A shadow root can be attached with an encapsulation mode, which is specified as either open or closed.
If the {mode: "open"} argument is passed, the host element's shadowRoot property can subsequently be used to get the attached shadow root.
This can be used to access elements in the Shadow DOM:
element.attachShadow({ mode: "open" });
element.shadowRoot; // Returns a ShadowRoot obj
If {mode: "closed"} is passed then the Element's shadowRoot property is set to null.
Note that JavaScript can still access a closed shadow root by storing the value returned by the function.
element.attachShadow({ mode: "closed" });
element.shadowRoot; // Returns null
Examples
>Word count custom element
The following example is taken from our word-count-web-component demo (see it live also).
You can see that we use attachShadow() in the middle of the code to create a shadow root, which we then attach our custom element's contents to.
// Create a class for the element
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();
// count words in element's parent element
const wcParent = this.parentNode;
function countWords(node) {
const text = node.innerText || node.textContent;
return text
.trim()
.split(/\s+/g)
.filter((a) => a.trim().length > 0).length;
}
const count = `Words: ${countWords(wcParent)}`;
// Create a shadow root
const shadow = this.attachShadow({ mode: "open" });
// Create text node and add word count to it
const text = document.createElement("span");
text.textContent = count;
// Append it to the shadow root
shadow.appendChild(text);
// Update count when element content changes
this.parentNode.addEventListener("input", () => {
text.textContent = `Words: ${countWords(wcParent)}`;
});
}
}
// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });
Disabling shadow DOM
If the element has a static property named disabledFeatures, which is an array containing the string "shadow", then the attachShadow() call will throw an exception.
For example:
class MyCustomElement extends HTMLElement {
// Disable shadow DOM for this element.
static disabledFeatures = ["shadow"];
constructor() {
super();
}
connectedCallback() {
// Create a shadow root.
// This will throw an exception.
const shadow = this.attachShadow({ mode: "open" });
}
}
// Define the new element
customElements.define("my-custom-element", MyCustomElement);
Named slot assignment
This example demonstrates named slot assignment.
Creating the web component
This code creates a web component that has three named slots for an article's title, metadata, and body section.
The ShadowRoot is attached in the custom element's constructor.
We don't need to explicitly set the option slotAssignment: "named" because it is the default.
class MyArticle extends HTMLElement {
constructor() {
super();
// Attach the shadow root
this.attachShadow({ mode: "open" /*, slotAssignment: "named"*/ });
}
connectedCallback() {
this.render();
}
render() {
// Define the internal structure and styles
this.shadowRoot.innerHTML = `
<style>
.header {
background-color: plum;
}
.meta {
background-color: green;
}
.body {
background-color: lightblue;
}
</style>
<h2 class="header">
<slot name="title"></slot>
</h2>
<div class="meta">
<slot name="meta"></slot>
</div>
<div class="body">
<slot></slot>
</div>
`;
}
}
// Register the component
customElements.define("my-article", MyArticle);
Using the web component
The HTML below uses the <my-article> web component we just created.
The nested elements are rendered in the component's slots based on name matching.
The unnamed elements are rendered in the component's unnamed slot (the body).
<my-article>
<span slot="title">Text for the title slot</span>
<span slot="meta">Text for the meta slot</span>
<p>
Text 1 with no slot attribute. Goes into default (unnamed) slot inside the
"body" div.
</p>
<p>
Text 2 with no slot attribute. Also goes into default (unnamed) slot inside
the "body" div.
</p>
</my-article>
Results
The example below should show the content of the slots displayed in the appropriate sections.
Unnamed slot assignment
This example demonstrates manual slot assignment.
With this approach, each element must be manually assigned to a particular slot using HTMLSlotElement.assign().
There is no default assignment, so any slot that is not assigned will be empty.
HTML
First we have a hidden support warning, displayed via JavaScript if the browser doesn't support slotAssignment: "manual".
<p id="support-warning" hidden>
⛔ Your browser doesn't support manual slot assignment (named assignment is
used).
</p>
Next, we define our <my-article> custom element with child elements for the title, metadata, and body content.
Each child is identified by id; unlike named slot assignment, no slot attribute is needed.
<my-article>
<span id="text_title">Text for the title slot</span>
<span id="text_meta">Text for the meta slot</span>
<p id="text_body_1">Text 1 for body slot.</p>
<p id="text_body_2">Text 2 for body slot.</p>
</my-article>
JavaScript
The custom element attaches a shadow root with slotAssignment: "manual".
The shadow DOM contains unnamed slots identified by id.
The assignSlots() method manually assigns the light DOM elements to the slots.
Note that multiple nodes can be assigned to a single slot — the order they are specified controls the render order.
class MyArticle extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open", slotAssignment: "manual" });
}
connectedCallback() {
this.render();
this.assignSlots();
}
render() {
this.shadowRoot.innerHTML = `
<style>
.header {
background-color: plum;
}
.meta {
background-color: green;
}
.body {
background-color: lightblue;
}
</style>
<h2 class="header">
<slot id="titleSlot"></slot>
</h2>
<div class="meta">
<slot id="metaSlot"></slot>
</div>
<div class="body">
<slot id="bodySlot"></slot>
</div>
`;
}
assignSlots() {
// 1. Target your slots
const titleSlot = this.shadowRoot.querySelector("#titleSlot");
const metaSlot = this.shadowRoot.querySelector("#metaSlot");
const bodySlot = this.shadowRoot.querySelector("#bodySlot");
// 2. Target your light DOM elements
const titleText = this.querySelector("#text_title");
const metaText = this.querySelector("#text_meta");
const body1Text = this.querySelector("#text_body_1");
const body2Text = this.querySelector("#text_body_2");
// 3. Manually assign them
titleSlot.assign(titleText);
metaSlot.assign(metaText);
bodySlot.assign(body2Text, body1Text);
}
}
customElements.define("my-article", MyArticle);
This code tests if the ShadowRoot.slotAssignment property is defined, and displays the warning if it is not.
const isSlotAssignmentSupported = Object.hasOwn(
ShadowRoot.prototype,
"slotAssignment",
);
document
.querySelector("p[hidden]")
.toggleAttribute("hidden", isSlotAssignmentSupported);
Results
The example below should show the content of the slots displayed in the appropriate sections.
Note:
If manual slot assignment is not supported, a warning is displayed and the browser will use named assignment.
However, because none of the light DOM elements have a slot attribute, they will all be inserted into the first unnamed slot (the title slot).
Specifications
| Specification |
|---|
| DOM> # dom-element-attachshadow> |
Browser compatibility
See also
ShadowRoot.modeShadowRoot.delegatesFocusShadowRoot.slotAssignment- Declaratively attach a shadow root with the
shadowrootmodeattribute of the<template>element - Declarative shadow DOM on web.dev (2023)