HTML Templates
This lesson explains HTML Templates as a core Web Components standard for storing reusable, inert markup that you clone into custom elements, Shadow DOM, and dynamic UIs.
What Are HTML Templates?
HTML Templates are one of the three core Web Components standards,
alongside Custom Elements and Shadow DOM. They let you define
reusable markup in the page that stays hidden until JavaScript
clones it into the live DOM.
The <template> element stores a
DocumentFragment in its content property.
That fragment is inert: scripts do not run,
images do not load, and styles do not apply until you clone and
insert the content.
| Feature | Description |
| Element | <template> |
| Content API | template.content |
| Default behavior | Inert and not rendered |
| Activation | cloneNode(true) then append |
| Primary use | Reusable component structure |
| Pairs with | Custom Elements and Shadow DOM |
Basic HTML Template Example
<template id="alert-template">
<div class="alert" role="alert">
<strong>Notice:</strong>
<span class="message"></span>
</div>
</template>
const template =
document.getElementById("alert-template");
const clone =
template.content.cloneNode(true);
clone.querySelector(".message").textContent =
"Profile saved successfully";
document.body.appendChild(clone);
The template markup never appears on its own. JavaScript clones
it, customizes the copy, and inserts the result into the document.
How HTML Templates Work
| Step | What Happens |
| 1. Declare template | Browser parses markup into an inert fragment |
| 2. Access content | template.content returns the fragment |
| 3. Clone fragment | cloneNode(true) creates a deep copy |
| 4. Customize clone | Update text, attributes, or event listeners |
| 5. Insert clone | Append into DOM, Shadow DOM, or another container |
Because the original template stays untouched, you can clone it
many times to create identical UI instances efficiently.
Inert Template Content
Inert content is the key reason templates exist. Without
inertness, hidden markup would still fetch images, run scripts,
and affect layout.
<template id="lazy-card">
<img src="photo.jpg" alt="User photo">
<script>console.log("This does not run yet");</script>
</template>
Nothing inside the template executes or renders until you clone
it. That makes templates safe for storing component blueprints in
the page.
Templates with Shadow DOM
The most common Web Components pattern clones a template into a
shadow root during construction.
class InfoCard extends HTMLElement {
constructor() {
super();
const template =
document.getElementById("info-card-template");
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(
template.content.cloneNode(true)
);
}
}
customElements.define("info-card", InfoCard);
<template id="info-card-template">
<style>
:host { display: block; }
.card { padding: 1rem; border: 1px solid #ccc; }
</style>
<article class="card">
<slot name="title"></slot>
<slot></slot>
</article>
</template>
<info-card>
<span slot="title">Shipping</span>
Arrives in 2–3 business days.
</info-card>
Templates keep component structure and scoped styles together while
Shadow DOM isolates them from the rest of the page.
Creating Multiple Instances
function createListItem(text) {
const template =
document.getElementById("list-item-template");
const clone =
template.content.cloneNode(true);
clone.querySelector(".label").textContent = text;
return clone;
}
const list = document.querySelector("ul");
["Home", "About", "Contact"].forEach((label) => {
list.appendChild(createListItem(label));
});
One template can power unlimited instances. Clone, customize, and
append — the original blueprint remains available for the next
item.
Templates vs Other Approaches
| Approach | Pros | Cons |
<template> | Inert, efficient cloning, real HTML | Requires DOM reference or import |
innerHTML | Quick for simple strings | Re-parses markup, no inert storage |
| Template literals | Flexible in JavaScript | Harder to maintain large markup |
createElement | Full programmatic control | Verbose for complex UI |
For Web Components, <template> is usually the
best default because it keeps markup declarative and cloning
efficient.
Static Template Inside a Component Class
You can also store a template reference as a static class property
so each instance shares one blueprint.
class TagList extends HTMLElement {
static template =
document.getElementById("tag-list-template");
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(
TagList.template.content.cloneNode(true)
);
}
}
This pattern avoids re-querying the DOM on every instance and
works well when the template is defined once in the document.
Templates and Slots
Templates often include <slot> elements so users
can pass content into predefined areas. Learn more in the
Slots lesson.
<template id="panel-template">
<section class="panel">
<header><slot name="heading"></slot></header>
<div class="body"><slot></slot></div>
</section>
</template>
Slots turn a static template into a flexible component shell that
accepts projected content from outside the shadow tree.
Templates in Lit
Lit uses JavaScript template literals instead of
<template> in HTML, but the idea is the same:
define reusable markup and render it efficiently.
import { LitElement, html, css } from "lit";
class StatusPill extends LitElement {
static properties = {
label: { type: String }
};
render() {
return html`<span class="pill">${this.label}</span>`;
}
}
Under the hood, Lit still manages efficient DOM updates. Native
<template> remains valuable when you want
markup defined in HTML or shared outside a JS bundle.
When to Use HTML Templates
- You need reusable markup for custom elements.
- You clone the same UI structure many times.
- You want inert storage before inserting into Shadow DOM.
- You prefer declarative HTML over large JS strings.
- You combine structure, styles, and slots in one blueprint.
- You build list items, cards, rows, or repeated widgets.
Common Template Use Cases
- Component shadow root markup and scoped styles.
- Dynamic lists rendered from API data.
- Modal, toast, and dialog shells.
- Table rows and data grid cells.
- Design system primitives with slot-based APIs.
- Server-rendered pages that hydrate custom elements.
HTML Template Best Practices
- Give every template a unique
id for easy lookup. - Always clone with
cloneNode(true) for deep copies. - Keep templates focused on structure, not business logic.
- Store shared templates once and reuse via static references.
- Include scoped
<style> when using Shadow DOM. - Use semantic HTML and ARIA roles inside templates.
- Prefer templates over repeated
innerHTML parsing.
Common Template Mistakes
- Appending
template.content directly without cloning. - Expecting template scripts to run before cloning.
- Assuming cloned nodes update the original template.
- Putting huge dynamic strings into templates instead of slots.
- Forgetting to query the clone, not the original fragment.
- Mixing global styles with shadow templates unintentionally.
- Re-parsing the same markup with
innerHTML repeatedly.
Key Takeaways
- HTML Templates are a core Web Components standard.
<template> stores inert, reusable markup. cloneNode(true) activates a copy in the live DOM. - Templates pair naturally with Shadow DOM and slots.
- One template can power many component instances.
- They are more efficient than re-parsing HTML strings.
Pro Tip
Define your component's shadow markup in a
<template> in HTML, then clone it in the
constructor. That keeps structure readable and makes the component
easier to maintain than long template literal strings.