How to Use HTML Templates and Slots with JavaScript
HTML templates and slots are built-in web standards designed to
create reusable, flexible UI components without rendering overhead. The
<template> element holds inert HTML markup that the
browser ignores until instantiated with JavaScript, while the
<slot> element acts as a placeholder inside a Shadow
DOM to inject custom user-provided markup. This guide explains what
templates and slots are, how they work together within the Document
Object Model (DOM), and how to manipulate them using vanilla
JavaScript.
What is the
<template> Element?
The <template> tag is an HTML mechanism for
holding client-side content that is not rendered when the page loads.
The content inside a <template> is stored as a
DocumentFragment. Because it is inert:
- Scripts inside it do not execute immediately.
- Images, audio, and video do not download or play until instantiated.
- Styles do not apply to the main document.
Instantiating a Template with JavaScript
To use the markup inside a <template>, access its
.content property, clone the DocumentFragment,
and append it to the active DOM.
<template id="user-card-template">
<div class="user-card">
<h3 class="user-name"></h3>
<p class="user-role"></p>
</div>
</template>
<div id="container"></div>// 1. Reference the template element
const template = document.getElementById('user-card-template');
// 2. Clone the template's content (true enables a deep clone)
const clone = template.content.cloneNode(true);
// 3. Manipulate the cloned elements before rendering
clone.querySelector('.user-name').textContent = 'Jane Doe';
clone.querySelector('.user-role').textContent = 'Software Engineer';
// 4. Append the clone to the document
document.getElementById('container').appendChild(clone);What is the <slot>
Element?
The <slot> tag acts as a dynamic placeholder
inside a Web Component’s Shadow DOM. It enables content projection (or
“slotting”), which allows external HTML markup provided by the consumer
to be rendered inside designated locations within the component’s
internal structure.
Types of Slots:
- Default (Unnamed) Slot: Catches any light DOM
elements that do not have a designated
slotattribute. - Named Slot: Specified using the
nameattribute (<slot name="header"></slot>). Content is targeted to this slot using the matchingslot="header"attribute in the light DOM.
Combining Templates, Shadow DOM, and Slots
Slots require a Shadow Root to project content properly. Here is how templates and slots operate inside a custom Web Component:
<!-- Component Template with Slots -->
<template id="custom-card-template">
<style>
.card { border: 1px solid #ddd; padding: 16px; border-radius: 8px; }
::slotted(h2) { margin-top: 0; color: #333; }
</style>
<div class="card">
<!-- Named slot for the header -->
<slot name="card-title">Default Title</slot>
<!-- Default slot for the body -->
<slot>Default body text goes here.</slot>
</div>
</template>
<!-- Custom Element Usage in the Document -->
<custom-card>
<h2 slot="card-title">Project Alpha</h2>
<p>This paragraph fills the default, unnamed slot.</p>
</custom-card>class CustomCard extends HTMLElement {
constructor() {
super();
// 1. Attach Shadow DOM
const shadowRoot = this.attachShadow({ mode: 'open' });
// 2. Retrieve and clone the template
const template = document.getElementById('custom-card-template');
const content = template.content.cloneNode(true);
// 3. Append cloned template containing slots to the Shadow Root
shadowRoot.appendChild(content);
}
}
// Register the custom element
customElements.define('custom-card', CustomCard);Manipulating Slots with JavaScript
JavaScript provides built-in methods and events to inspect and interact with slotted content dynamically.
1.
Inspecting Slotted Content (assignedNodes and
assignedElements)
You can query which nodes or elements are assigned to a slot from inside the Shadow Root:
const slotElement = shadowRoot.querySelector('slot[name="card-title"]');
// Returns all nodes (including text and whitespace)
const nodes = slotElement.assignedNodes();
// Returns only element nodes (e.g., HTML tags)
const elements = slotElement.assignedElements();
console.log(elements[0]); // Outputs: <h2 slot="card-title">Project Alpha</h2>2. Accessing the Assigned Slot from Light DOM
If you have a reference to an element in the light DOM, you can find
the <slot> element it is mapped to using the
assignedSlot property:
const heading = document.querySelector('h2[slot="card-title"]');
console.log(heading.assignedSlot); // Returns the corresponding HTMLSlotElement3. Listening for Slot
Changes (slotchange)
The slotchange event fires on a
<slot> element whenever its assigned nodes are added,
removed, or replaced:
const defaultSlot = shadowRoot.querySelector('slot:not([name])');
defaultSlot.addEventListener('slotchange', (event) => {
const updatedNodes = defaultSlot.assignedElements();
console.log('Slot content updated:', updatedNodes);
});