On this page

Slots

When you use a web component you can configure it using attributes, using them like parameters to adjust how it works. You can also insert your own HTML inside the web component, changing how it looks, customizing it with your own data. To allow this type of customization, you can design your web component with special areas inside, known as slots, where the custom HTML will be slotted into.

Think of it in this way. There is the creator of the web component, who includes areas inside, that can contain outside HTML. Then there is the user of the web component, who sets the custom HTML, that will be slotted into those inner areas. It helps to understand what it going on if you see the different parts existing in either one location or the other. The best way to get your head around all this, is to look at some examples.

How it Works

Let's create an example web component that uses a slot.

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

    // Attach shadow DOM root
    this.attachShadow({ mode: 'open' });

    // Set inner HTML
    this.shadowRoot.innerHTML = '<b><slot name="make-bold"></slot></b>';
  }
}
customElements.define('slot-example', SlotExample);  

The first thing that is required is a shadow DOM. Slots only work inside a shadow DOM. The next thing we do is to set the inner HTML. For this example we put a slot inside some bold tags. The slot is given a name make-bold. Let's see how it would be used.

<slot-example>
  <span slot="make-bold">Slot example text.</span>
</slot-example>

We use the slot-example tag, which is linked to our web component, and we include some extra details inside them. This is where the custom HTML needs to be put, which will be slotted into the web component area. We have a simple span tag (it can be any tag), with an attribute named slot, with the value make-bold.

This ends up telling the browser to take the HTML element with the slot attribute make-bold and insert it into the that <slot> area inside the web component. This is what the DOM looks like.

<!DOCTYPE html>
<html lang="en">
  <head>...</head>
  <body>
    <p>...</p>
    <slot-example>
      #shadow-root (open)
        <b>
          <slot name="make-bold">
            <span>slotted
          </slot>
        </b>
      <span slot="make-bold">Slot example text.</span>slot
    </slot-example>
  </body>
</html>  

There are two points of interest here. First, inside the web component you have the slot, but it does not contain the slotted HTML, only a reference to it. Second, outside the shadow DOM, but inside the web component, is the <span slot="make-bold"> tag, but this is not show at that location in the DOM, but it does give a reference to the slot it is linked to.

What is happening, is that the outside slot element it being virtually copied and inserted inside the web component's slot area, and then shown in the browser. The two parts are separate but linked together.

Take a closer look and the example below. Use the browser debug tools to take a good look at what is happening.

Default Slot

You can use more than one slot inside your web component. Each one needs to have a unique name. However, one of the slots can be the default one, a nameless slot. Here is the inner HTML of the example web component.

<p>Before slot.</p>
<slot></slot>
<p>After slot.</p>  

We have put a <slot> tag in the middle of two paragraphs. The slot tag has no name attribute, and therefore becomes the default one. Let's see how it is used.

<default-slot>
  Everything in there is copied into the default slot.
</default-slot>

As you can see, there are no sub element tags, only text. But even if you have extra tags, all of it would get virtually copied into the default slot inside the web component. We did not need to use a slot attribute with a name value.

<!DOCTYPE html>
<html lang="en">
  <head>...</head>
  <body>
    <p>...</p>
    <default-slot>
      #shadow-dom (open)
        <p>Before slot.</>
        <slot>
          [slotted]#text
        </slot>
        <p>After slot.</p>
      " Everything in there is copied into the default slot. "
    </default-slot>
  </body>
</html>

Without the default slot inside the web component, all non-slot based tags would not become part the web component and would therefore not be shown.

Multiple Slots

Your web component can contain more than one named slot area. Someone using your web component will be able to put one set of custom HTML into one area and another set of custom HTML into a different area. The best way to understand this, is with another example.

<b><slot name="make-bold"></slot></b><br>
<i><slot name="make-italic"></slot></i><br>
default text is = [<slot></slot>]

The inner HTML of the web component has 3 slots. The first is the make-bold, which sits inside some bold tags. Next is the make-italic, that sits between some italic tags. And finally is the nameless default slot. Let's see it being used.

<multiple-slot>
<span slot="make-italic">Make this text italic.</span>
<span slot="make-bold">Make this text bold.</span>
Hello.
</multiple-slot>

Between the web component tags we have 3 parts. The first is a span with the slot named to make-italic. The next is another span but with a slot named to make-bold. Then there is the final Hello. text. Notice how we have listed the make-bold and make-italic in a different order than it was listed in the web component. Let's see what this looks like.

Looking at the final elements in the DOM, the order of the slots match how they are listed in the web component's inner HTML, not how they are being used.

<!DOCTYPE html>
<html lang="en">
  <head>...</head>
  <body>
    <p>...</p>
    <multiple-slot>
      #shadow-dom (open)
        <b>
          <slot name="make-bold">
            <span>slotted
          </slot>
        </b>
        <br>
        <i>
          <slot name="make-italic">
            <span>slotted
          </slot>
        </i>
        <br>
        "default text is = ["
        <slot>
          #textslotted
        </slot>
        "]"
      <span slot="make-italic">Make this text italic.</span>slot
      <span slot="make-bold">Make this text bold.</span>slot
      " Hello. "slot
    </multiple-slot>
  </body>
</html>  

The browser is taking all the slots named make-bold and making a virtual copy of them, and putting them into the make-bold slot area inside the web component. It does not matter where inside the web component's tags they are listed.

Duplicate Slots

A web component's slot does not need to be used at all if you don't want to. You could leave it blank, missing, unused. You can use it just the once, as we have shown already, using it like some type of one to one relationship. But you can also use the same slot multiple times. Let's take a look at how this would work.

<p>Before slot.</p>
<b><slot name="make-bold"></slot></b>
<p>After slot.</p>  

The HTML used inside the web component has a before and after paragraph with the named slot between them both. Let's see what happens if we use the slot more than once.

<duplicate-slot>
  <div slot="make-bold">First text to be slotted.</div>
  <div slot="make-bold">Second text to be slotted.</div>
</duplicate-slot>

Inside the web component's tags we have 2 DIV tags, both using the same named slot. How will the two custom HTML parts be slotted into the web component's slot?

The final result is that both slot items (first and second) are grouped together, and a virtual copy is make of them and put into the single slotted area inside the web component.

<!DOCTYPE html>
<html lang="en">
  <head>...</head>
  <body>
    <p>...</p>
    <duplicate-slot>
      #shadow-dom (open)
        <p>Before slot.</p>
        <b>
          <slot name="make-bold">
            <div>slotted
            <div>slotted
          </slot>
        </b>
        <p>After slot.</p>
      <div slot="make-bold">First text to be slotted.</div>slot
      <div slot="make-bold">Second text to be slotted.</div>slot
    </duplicate-slot>
  </body>
</html>  

What we do not end up with, are 2 sets of "before" and "after" parts, one for each slotted DIV. They are still separate slotted DIV elements, they are not grouped together into one single slot or element, which means you can still treat each one individually.

Double Placed Slots

Let's see what happens if we try to use the same named slot inside a web component. A named slot must be unique but what happens if it isn't. Take a look at the inner HTML of out test web component.

<p>First</p>
<b><slot name="make-bold"></slot></b><br>
<p>Second</p>
<b><slot name="make-bold"></slot></b><br>
<p>Third</p>

Here we have 3 paragraphs, with the same named slot used twice. Let's use it and see what happens.

<double-placed-slot>
<span slot="make-bold">Make this text bold.</span>
Hello.
</double-placed-slot>

Inside the web component's tags we use the named slot. We also have some unnamed default slot text used. If you were hoping that the named slot would end up in both places inside the web component, then sadly you are out of luck. Take a look at the example running.

Having a closer look at the final DOM will help us understand what has happened.

<!DOCTYPE html>
<html lang="en">
  <head>...</head>
  <body>
    <p>...</p>
    <double-placed-slot>
      #shadow-dom (open)
        <p>First</p>
        <b>
          <slot name="make-bold">
            <span>slotted
          </slot>
        </b>
        <br>
        <p>Second</p>
        <b>
          <slot name="make-bold">
          </slot>
        </b>
        <br>
        <p>Third</p>
      <span slot="make-bold">Make this text bold.</span>slot
      " Hello. "
    </double-placed-slot>
  </body>
</html>

The first slot has a slotted copy linked to it, but the second slot has nothing linked to, it is empty, because the slot name has already be used.

You will also notice that the "Hello." text is not shown. This is because the default nameless slot does not exist inside the web component, and therefore there is nowhere to copy it into.

Unused Slots

What happens if our web component has a named slot that is not used? What if we use a slot name that the web component doesn't use? Take a look at the inner HTML of this test web component.

<b><slot name="make-bold"></slot></b><br>
<i><slot name="make-italic"></slot></i><br>
default text is = [<slot></slot>]  

We have 2 named slots and the nameless default one. Let's use it so that only one of the slots are used.

<unused-slot>
  <span slot="make-italic">Make this text italic.</span>
  <span slot="unknown-slot">Unknown slot text.</span>
</unused-slot>

Only the make-italic slot is being used, the other two are not. We are also trying to use an unknown slot. Take a look at the final result.

<!DOCTYPE html>
<html lang="en">
  <head>...</head>
  <body>
    <p>...</p>
    <unused-slot>
      #shadow-dom (open)
        <b>
          <slot name="make-bold">
          </slot>
        </b>
        <br>
        <i>
          <slot name="make-italic">
            <span>slotted
          </slot>
        </i>
        <br>
        "default text is = ["
        <slot>
        </slot>
        "]"
      <span slot="make-italic">Make this text italic.</span>slot
      <span slot="unknown-slot">Unknown slot text.</span>
    </unused-slot>
  </body>
</html>

There is a gap where the make-bold slot is. This is because there are no slots to link to it and it will just show an empty space. The make-italic slot is shown as you would except. Because there is no nameless default parts to copy over, that area is empty too. You will not see the unknown slot part either, because it is a named slot (not default) but that slot does not exist in the web component, and therefore there is nowhere to copy it to.

When you design your web components to use slots, you need to approach it in a may that allows it to be used incorrectly, and work around the issues that it could create. Make sure all slots are not mandatory, or if they are, then show some sort of useful warning message.

Access to Slot Elements

There are some limitations when it comes to accessing the elements that have been slotted into a web component's slot. But before we start looking into how we do get access to them, let's first explore how we do not do it. To show this, let's look at an example.

<b><slot id="slot-id" name="make-bold"></slot></b><br>
<button id="check-ids">Check for IDs (inside)</button>

This is the inner HTML of the test web component. We have given the slot an id value of slot-id. This will be used to see if we can access it from within the web component, and from outside it too. There is also a button that will be used to help us see if we have access to the elements we are looking for. Before we look at the button's click event, let's see how the web component is being used.

<slot-element>
  <span id="slotted-id" slot="make-bold">Slot example text.</span>
</slot-element>

Inside the web component tags we have a span tag using the slot name make-bold. This is all very normal so far. You will notice that we have given it the id value of slotted-id. This means we have two elements that we are interested in. The outside element, the slotted part, with the id value of slotted-id and the inside element, the inner slot part, with the id value of slot-id.

We are going to try to access both elements, from both inside the web component, and from outside it. Let's first look at the inside button click event.

_checkIdsButtonClick(event) {
  // Look for slot element
  const slotElement = this.shadowRoot.getElementById('slot-id');

  // Report the result
  if (slotElement !== null) {
    // Log event
    window.customLogEvent('INSIDE: slot element found');
  } else {
    // Log event
    window.customLogEvent('INSIDE: slot element not found');
  }

  // Look for slotted element
  const slottedElement = this.shadowRoot.getElementById('slotted-id');

  // Report the result
  if (slottedElement !== null) {
    // Log event
    window.customLogEvent('INSIDE: slotted element found');
  } else {
    // Log event
    window.customLogEvent('INSIDE: slotted element not found');
  }
}

The first thing we do is to try to access the inner slot element, slot-id, and then report on whether it was found or not. Notice that we are using the shadowRoot.getElementById function, so we are only searching the internal shadow DOM area for the element. The next thing we do is to see if we can get access to the outer slotted element, slotted-id, and then report on that too. Again we are using the same function for the search. We are testing to see if we have access to the virtual copy of the outer HTML that was slotted into the slot area.

We also have a button on the outer part of the HTML that tests to see if we have access to the same two elements, but from outside the web component.

window.checkForIdsClick = function () {
  // Look for slot element
  const slotElement = document.getElementById('slot-id');

  // Report the result
  if (slotElement !== null) {
    // Log event
    window.customLogEvent('OUTSIDE: slot element found');
  } else {
    // Log event
    window.customLogEvent('OUTSIDE: slot element not found');
  }

  // Look for slotted element
  const slottedElement = document.getElementById('slotted-id');

  // Report the result
  if (slottedElement !== null) {
    // Log event
    window.customLogEvent('OUTSIDE: slotted element found');
  } else {
    // Log event
    window.customLogEvent('OUTSIDE: slotted element not found');
  }
};

This is very similar to the button click event used inside the web component, but here we are using the document.getElementById function instead. This is searching the whole document to see if those elements, with those id values, are accessible. Take a look at the final example.

Pressing the Check for IDs (inside) button will report that access to the inner slot element is available, but access to the slotted part is not. This happens because there is nothing inside the slot element itself, only a link to the slotted HTML that exists outside the web component.

Pressing the Check for IDs (outside) button will report something different. It will find that it has no access to the inner slot element, which you would expect because it is inside a shadow DOM, which hides everything from the outside. It also finds that it does have access to the outer slotted HTML element. This happens because the slotted HTML exists outside the web component's shadow DOM, making it part of the outer DOM (which in this case is the document DOM).

The important thing we can learn from trying this, is that a web component has no direct access to the HTML elements that have been slotted into one of its slots. They exist outside and cannot been accessed. However there is a way to get a list of all the elements that have been slotted into a web component slot.

The correct way to get access to the slotted elements is to use a HTMLSlotElement function called assignedElements. You first need to get the inner slot element, then call this function to get a list of slotted elements. Let's look at another example.

<b>
  <slot id="slot-id" name="make-bold"></slot>
</b>
<br>
<button id="assigned-slots">Get Assigned Slots</button>

This inner HTML of the web component has the make-bold slot, with an id value, and a button. When the button is pressed we do the following.

_assignedSlotsButtonClick(event) {
  // Look for slot element
  const slotElement = this.shadowRoot.getElementById('slot-id');

  // Get assigned elements
  const assignedElements = slotElement.assignedElements();

  // For each assigned element
  assignedElements.forEach(element => {
    // Log event
    window.customLogEvent(element.nodeName + ': ' + element.innerText);
  });
}  

The first thing we do is to get the slot element. Then we call its assignedElements function to get a list of all the slotted elements placed inside it. After this we loop through them and log some information about each of the elements.

The example shows that the function returns a list of each of the slotted elements, the DIV elements with their inner text.

Once you have the element you can do whatever you want with it. From the web component's point of view it is best to treat the elements as belonging to someone else and therefore not to keep a record of them for later use.

Slot Element Events

Because the slotted elements do not really belong to the web component, you should not add events to them. One of the reasons for this, is that you have no means of removing the events, because there is no way of knowing when the slotted element is being removed (from the web component).

So how do you handle events they have? If you have a web component, with a slot area, with slotted in elements, that when pressed does something, how do you monitor those click events? Let's look at an example.

<b>
  <slot id="slot-id" name="make-bold"></slot>
</b>';

We have the same make-bold slot as we have seen before. We have given it an id value too. We can use this to add a click event listener to the slot element.

connectedCallback() {
  // Get slot element
  this._slotElement = this.shadowRoot.getElementById('slot-id');

  // Add click event
  this._slotElement.addEventListener('click', this._click);
}

_click(event) {
  // Log event
  window.customLogEvent('Slotted clicked: ' + event.target.innerText);
}  

The click event function will report the event with the inner text of whichever element was clicked. Have a look and see what happens.

When you click one of the outer slotted HTML elements, the click event will bubble up into the inner slot element, where our click event is watching. Even though the outer slotted element is not directly part of the inner slot element, the event travels up the list of sub-elements, crosses the web component shadow DOM barrier, and finds the inner slot element click event. The event parameter will give us information about which of the outer slotted elements was clicked (the target).

Slot Change Event

An inner slot element has a special event it fires whenever something has been slotted into it, or removed. When using a web component, you normally list all the slotted parts in HTML and then never change them afterwards. But you can add and remove slotted elements when you need. When this happens, it can be useful to know about it, from inside the web component.

<b>
  <slot id="slot-id" name="make-bold"></slot>
</b>

We have are normal make-bold slot with an id value. To this we add the slotchange event.

connectedCallback() {
  // Get slot element
  this._slotElement = this.shadowRoot.getElementById('slot-id');

  // Add slot change event
  this._slotElement.addEventListener('slotchange', this._slotchange);
}

disconnectedCallback() {
  // Remove slot change event
  this._slotElement.removeEventListener('slotchange', this._slotchange);
}

_slotchange(event) {
  // Log event
  window.customLogEvent('slot change');
}

When the web component connects to the DOM, we find the slot element, and add the slotchange event. We also remove the event when the web component is removed from the DOM. The slot change event just logs that it has been fired.

<slot-change id="slot-change-id">
  <div id="first-slotted" slot="make-bold">First slotted.</div>
  <div id="second-slotted" slot="make-bold">Second slotted.</div>
</slot-change>

We start the web component with two inner slotted tags. We want to see if the slotchange event will fire at the start when the first two slotted elements are copied over. We also include in the example (not shown here) some buttons, one that will remove the second slotted element, and another to add a third slotted element. Take a look at the example.

The first thing you will see, is that the slot change event is fired at the start, when the pre-defined slotted elements are made part of the web component. After pressing one of the buttons, to remove or add a slotted element, the slot change event is fired again, as you would except.

This event can sometimes be useful if you need to make changes to the inner workings of the web component, based on the slotted elements.