Alligator.io
With Shadow DOM, we can create Web Components that are a composition between the component’s internals and elements provided at author-time by the user of the custom element. This is similar with using either option or optgroup elements inside a native select element. This composition is made possible using a mechanism known as slots and here we’ll go over how to use slots and named slots in your custom elements to allow for interesting compositions.
Allowing users of your custom elements to add their own markup and elements as part of your element is as simple as using the slot element in your component’s template.
Here’s an example of a dumb custom element that only acts as a styled shell for content that’s added when the element is used:
(function() {
const template = document.createElement('template');
template.innerHTML = `
<style>
:host {
display: block;
contain: content;
text-align: center;
background: papayawhip;
max-width: 500px;
margin: 0 auto;
box-shadow: 0 0 10px rgba(128, 100, 38, 0.34);
border-radius: 8px;
border: 2px dashed #ccc049;
}
</style>
<span class="code-annotation"><slot></slot></span> `;
class MyInfoBox extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
} }
Wondering about the use of :host for our element’s style? Read about styling custom elements.
This element can then be used like this:
<my-info-box>
<p>I'm slotted content!</p>
</my-info-box>
The markup added inside a custom element is called the light DOM and it stays outside of the Shadow DOM’s reach, as you can see from this screenshot of the element as seen from Chrome’s DevTools:
Elements in the light DOM are instead accessible directly as children of the element, so you could do something like this to grab an element, as you would normally to access an element in the DOM:
const myInfoBox = document.querySelector('my-info-box');
const text = myInfoBox.children[0].innerText;
console.log(text); // I'm slotted content!
A light DOM element that’s being used in a slot is known as a distributed node.
You can provide default content for a slot, in case none is provided when the element is used:
<slot>
<p>I'm some default content!</p>
</slot>
And this default content will be used if the element is used like this:
<my-info-box></my-info-box>
Creating more complex elements that can take various pieces of content from the element’s user can be done easily using named slots. You can see a simple example here where a named slot is used alongside a regular slot:
<div>
<slot name="title"></slot>
<hr>
<slot></slot>
</div>
The element can then be used like this:
<my-info-box>
<span slot="title">🍭 Fancy title</span>
<p>I'm going straight to the anonymous slot.</p>
</my-info-box>
Thanks to named slots, you can easily create a complex custom elements that compose multiple pieces together. For example, a nav bar that takes a title, a logo, left navigation items and right navigation items.
We can style slotted content using the ::slotted() selector. Here’s a very simple example:
<style>
::slotted(span) {
background: pink;
}
::slotted(.content) {
font-family: monospace;
}
</style>
<div>
<slot name="title"></slot>
<hr>
<slot></slot>
</div>
And, with this, the title in the following example will have a pink background and the main content will be in a monospace font:
<my-info-box>
<span slot="title">🍭 Fancy title</span>
<p class="content">I'm going straight to the anonymous slot.</p>
</my-info-box>
Note that the selector used with ::slotted should select a top-level element, as it can’t match a slot using a nested element.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!