Template Element
This lesson explains the HTML template element and how to use it with Web Components to store reusable, inert markup that can be cloned safely into custom elements and Shadow DOM.
What Is the HTML Template Element?
The <template> element holds HTML markup that is
not rendered immediately. Its contents are inert, meaning scripts
do not run, images do not load, and the browser does not display
the markup until you clone it with JavaScript.
In Web Components, templates are commonly used to define reusable
structure and styles for custom elements, especially when cloning
markup into Shadow DOM.
| Feature | Description |
| Element | <template> |
| Content Property | template.content |
| Default State | Inert and not rendered |
| Clone Method | cloneNode(true) |
| Common Use | Reusable component markup |
| Works With | Custom Elements and Shadow DOM |
Basic Template Example
<template id="card-template">
<article class="card">
<h2><slot name="title"></slot></h2>
<p><slot></slot></p>
</article>
</template>
const template =
document.getElementById("card-template");
const clone =
template.content.cloneNode(true);
document.body.appendChild(clone);
The template stays hidden in the page until JavaScript clones its
content and inserts the copy into the live DOM.
How the Template Element Works
| Step | What Happens |
| 1. Define template | Markup is stored but not displayed |
| 2. Access content | template.content returns a DocumentFragment |
| 3. Clone content | cloneNode(true) creates a reusable copy |
| 4. Insert clone | Append into light DOM or Shadow DOM |
| 5. Reuse template | Original template remains available for more clones |
Using Templates in Custom Elements
<template id="alert-template">
<div class="alert" role="alert">
<strong></strong>
<span></span>
</div>
</template>
class AlertBox extends HTMLElement {
connectedCallback() {
const template =
document.getElementById("alert-template");
const node =
template.content.cloneNode(true);
node.querySelector("strong").textContent =
this.getAttribute("title") || "Notice";
node.querySelector("span").textContent =
this.getAttribute("message") || "";
this.appendChild(node);
}
}
customElements.define("alert-box", AlertBox);
<alert-box
title="Success | PHPKINGDOM"
message="Profile updated successfully."
></alert-box>
Templates let you separate markup structure from component logic.
The custom element clones the template and fills in dynamic values.
Templates with Shadow DOM
<template id="profile-card-template">
<style>
:host {
display: block;
}
.card {
padding: 1rem;
border: 1px solid #dbe5f1;
border-radius: 0.75rem;
}
</style>
<article class="card">
<slot name="avatar"></slot>
<slot name="name"></slot>
<slot></slot>
</article>
</template>
class ProfileCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
const template =
document.getElementById("profile-card-template");
this.shadowRoot.appendChild(
template.content.cloneNode(true)
);
}
}
customElements.define("profile-card", ProfileCard);
This is one of the most common Web Components patterns: clone a
template into an open shadow root to combine encapsulation with
reusable markup.
Cloning Templates Multiple Times
const rowTemplate =
document.getElementById("row-template");
const users = [
{ name: "Alex", role: "Admin" },
{ name: "Sam", role: "Editor" },
{ name: "Riya", role: "Viewer" }
];
const list = document.querySelector("#user-list");
users.forEach((user) => {
const row =
rowTemplate.content.cloneNode(true);
row.querySelector(".name").textContent = user.name;
row.querySelector(".role").textContent = user.role;
list.appendChild(row);
});
One template can produce many DOM instances. The original template
remains intact, which makes templates efficient for lists, tables,
and repeated UI patterns.
Template vs innerHTML
| Feature | <template> | innerHTML |
| Markup Storage | Structured DOM fragment | HTML string |
| Initial Rendering | Not rendered until cloned | Parsed immediately when assigned |
| Reusability | Easy to clone many times | Must rebuild string each time |
| Performance | Better for repeated structures | Can reparsed unnecessarily |
| Slots Support | Works naturally with slots | Harder to manage with slots |
| Best For | Component libraries and Shadow DOM | Small one-off dynamic snippets |
Inline Template Inside a Component
class StatusBadge extends HTMLElement {
constructor() {
super();
const template = document.createElement("template");
template.innerHTML = `
<style>
span {
padding: 0.25rem 0.5rem;
border-radius: 999px;
font-size: 0.875rem;
}
</style>
<span><slot></slot></span>
`;
this._template = template;
}
connectedCallback() {
this.appendChild(
this._template.content.cloneNode(true)
);
}
}
You do not always need a template in the HTML document. Many
components create a template programmatically in the constructor
and reuse it during the component lifecycle.
Templates with Slots
<template id="panel-template">
<section class="panel">
<header>
<slot name="title">Default Title</slot>
</header>
<div class="body">
<slot>Default content</slot>
</div>
</section>
</template>
<app-panel>
<h2 slot="title">Notifications</h2>
<p>You have 3 unread messages.</p>
</app-panel>
Templates work well with slots because the cloned structure can
define projection points while consumers supply content through
the light DOM.
Template Element Properties and Methods
| API | Description |
template.content | Read-only DocumentFragment with template children |
cloneNode(true) | Deep clone of the template fragment |
getElementById() | Find a template in the document |
querySelector() | Query inside cloned template content |
appendChild() | Insert cloned content into DOM or shadow root |
When to Use the Template Element
- You need reusable markup for custom elements.
- You clone the same structure many times in a list or grid.
- You want to avoid repeated HTML string parsing.
- You build Shadow DOM components with internal structure and CSS.
- You want inert markup that does not render before JavaScript runs.
- You combine templates with slots for composable components.
Common Template Use Cases
- Cards, alerts, badges, and panels in a design system.
- Table rows and list items generated from data.
- Modal dialogs and drawer layouts in Shadow DOM.
- Form field wrappers with consistent internal markup.
- Repeated UI blocks in documentation demos and playgrounds.
- Component libraries that ship one template per element.
Template Best Practices
- Keep one template per component structure when possible.
- Clone templates instead of rewriting HTML strings repeatedly.
- Store templates in the constructor for reusable class instances.
- Use semantic HTML inside templates for accessibility.
- Combine templates with Shadow DOM for encapsulated styling.
- Give document-level templates clear IDs such as
card-template. - Query and update cloned nodes before appending them when needed.
Common Template Mistakes
- Appending
template.content directly without cloning. - Assuming template scripts run before cloning.
- Using
innerHTML everywhere when a template would be cleaner. - Creating a new template on every render instead of reusing one.
- Forgetting to populate cloned nodes before inserting them.
- Putting interactive markup in templates without accessibility planning.
- Mixing light DOM and Shadow DOM template strategies inconsistently.
Key Takeaways
- The
<template> element stores inert, reusable markup. - Use
template.content.cloneNode(true) to create live DOM instances. - Templates are ideal for custom elements and Shadow DOM structures.
- They improve reuse and performance compared with repeated HTML strings.
- Templates and slots work together to build composable Web Components.
Pro Tip
Define a component's markup once in a template, clone it in
connectedCallback() or the constructor, and keep dynamic
values in attributes or properties. That pattern scales well for
design systems and reusable libraries.