Composing Custom Elements With Slots And Named Slots

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.

Your First Slotted Content

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:

my-info-box.js

(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>

    <slot></slot>
  `;

  class MyInfoBox extends HTMLElement {
    constructor() {
      super();

      this.attachShadow({ mode: 'open' });
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
  }

  window.customElements.define('my-info-box', MyInfoBox);
})();

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:

Shadow DOM vs Light DOM

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.

Default Content

You can provide default content for a slot, in case none is provided when the element is used:

my-info-box.js (partial)

<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>

Named Slots

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:

my-info-box.js (partial)

<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.

Styling Slotted Content With ::slotted

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.

✖ Clear

🕵 Search Results

🔎 Searching...