Skip to main content

Slots

In this article, we will discuss slots in the context of UI5 Web Components.

Components often need to render children in specific locations in their shadow root, allowing applications to supply child content when using the component.

Currently, there are two types of slots: "named" and "unnamed". The difference between these types is that named slots have accessors as class members, while unnamed slots do not.

Unnamed slots​

Use unnamed slots when your component does not need to know if any children have been passed to a certain slot, or generally interact with its children from the said slot.

To define an unnamed slot, you simply add a <slot> element inside your .hbs template, for example:

<slot name="mySlot"></slot>

Note: It is recommended to describe your unnamed slots inside a JSDoc comment that describes your class using the @slot tag, following the pattern @slot {type} name - description.

Named slots and the @slot decorator​

Contrary to unnamed slots, named slots are used whenever the component must interact with its children in order to render itself properly.

To define your own named slot, you need to:

  • Use the slot decorator.
  • Define a class member.

The slot decorator is a property decorator that takes one optional argument as an object literal containing configuration options for the slot.

Defining a slot with the slot decorator means that this slot will be managed by the library. This means:

  • If any of this UI5 Web Component's children are custom elements, the framework will wait until they are all defined and upgraded before rendering the component.
  • The library will invalidate this UI5 Web Component whenever its children are added, removed, or rearranged (and additionally when invalidated, if invalidateOnChildChange is set).
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";

@customElement("my-demo-component")
class MyDemoComponent extends UI5Element {
@slot({ type: HTMLElement })
mySlot!: Array<HTMLElement>;
}

You can see the available options below.

type​

This option is required and accepts a type constructor (e.g., HTMLElement, Node) and is used to define the type of children that can be slotted inside the slot.

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";

@customElement("my-demo-component")
class MyDemoComponent extends UI5Element {
@slot({ type: HTMLElement })
mySlot!: Array<HTMLElement>;
}

Available types:

TypeDescription
HTMLElementAccepts HTML Elements only
NodeAccepts both Text nodes and HTML Elements

Default Slot​

This option accepts a boolean value and is used to define whether this slot is the default one.

Note: The default slot is defined simply as an empty slot tag: <slot></slot> (without a name attribute).

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";

@customElement("my-demo-component")
class MyDemoComponent extends UI5Element {
@slot({ type: HTMLElement, default: true })
mySlot!: Array<HTMLElement>;
}

individualSlots​

This option accepts a boolean value and determines whether each child will have its own slot, allowing you to arrange or wrap the children arbitrarily. This means that you need to handle the rendering on your own.

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";

@customElement("my-demo-component")
class MyDemoComponent extends UI5Element {
@slot({ type: HTMLElement, individualSlots: true })
mySlot!: Array<HTMLElement>;
}

To render individual slots, you have to iterate all children in that slot and use the _individualSlot property that the framework sets automatically set on each child:

{{#each mySlot}}
<slot name="{{this._individualSlot}}"></slot>
{{/each}}

Note: When this option is set to true, the _individualSlot property is set to each direct child, where _individualSlot returns a string following the pattern {nameOfTheSlot}-{index} and the slot attribute is changed based on that pattern.

invalidateOnChildChange​

This option accepts a boolean value or an object literal containing a configuration with more specific settings, determining whether the component should be invalidated on child change.

NOTE: This is an experimental option and should not be used.

Important: invalidateOnChildChange is not meant to be used with standard DOM elements and is not to be confused with MutationObserver-like functionality. It targets the use case of components that slot abstract items (UI5Element instances without a template) and require invalidation whenever these items are invalidated.

import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";

@customElement("my-demo-component")
class MyDemoComponent extends UI5Element {
@slot({ type: HTMLElement, invalidateOnChildChange: true })
mySlot!: Array<HTMLElement>;

@slot({ type: HTMLElement, invalidateOnChildChange: { properties: true, slots: false }})
mySlot2!: Array<HTMLElement>;

@slot({ type: HTMLElement, invalidateOnChildChange: { properties: ["myProp"], slots: ["anotherSlot"] }})
mySlot3!: Array<HTMLElement>;
}