⚡IntermediateHTML APIs and Web Components
The Template Element
Define reusable HTML fragments with <template> that are parsed but not rendered until cloned.
Overview
The <template> element holds HTML that is parsed and validated by the browser but not rendered it is inert. You can clone its content with JavaScript to stamp out multiple instances of a UI pattern efficiently.
Before <template>, developers used techniques like innerHTML, hidden divs, or script tags with custom types to store HTML templates. The <template> element is the semantic, standards-based solution.
Key characteristics:
- The browser parses and validates the HTML inside <template>
- The content is NOT rendered, executed, or displayed
- Scripts inside don't run, images don't download, styles don't apply
- You access the content via template.content (a DocumentFragment)
- Clone with template.content.cloneNode(true)
Key Points
- •<template> is parsed but invisible and inert scripts don't run, images don't load
- •template.content returns a DocumentFragment containing the template's children
- •cloneNode(true) deep-clones the fragment to create a new instance
- •Templates can be named with an id for easy selection
- •Good for repeating card/list item patterns stamped by JavaScript
- •Templates are available anywhere in the document but conventionally in <head> or at end of <body>
Examples
Product card template
<!-- Template definition (not rendered) -->
<template id="product-card-tmpl">
<li class="product-card">
<img class="card-img" src="" alt="">
<h3 class="card-name"></h3>
<p class="card-price"></p>
<button class="add-to-cart">Add to Cart</button>
</li>
</template>
<!-- Target container -->
<ul id="product-list"></ul>
<script>
const tmpl = document.getElementById('product-card-tmpl');
const list = document.getElementById('product-list');
const products = [
{ id: 1, name: 'Widget Pro', price: '$29.99', img: 'w1.jpg' },
{ id: 2, name: 'Gadget X', price: '$49.99', img: 'g1.jpg' },
];
products.forEach(product => {
const clone = tmpl.content.cloneNode(true); // deep clone
clone.querySelector('.card-img').src = product.img;
clone.querySelector('.card-img').alt = product.name;
clone.querySelector('.card-name').textContent = product.name;
clone.querySelector('.card-price').textContent = product.price;
list.appendChild(clone);
});
</script>Notification toast template
<template id="toast-tmpl">
<div class="toast" role="status" aria-live="polite">
<span class="toast-message"></span>
<button class="toast-close" aria-label="Dismiss">×</button>
</div>
</template>
<div id="toast-container"></div>
<script>
function showToast(message) {
const tmpl = document.getElementById('toast-tmpl');
const clone = tmpl.content.cloneNode(true);
clone.querySelector('.toast-message').textContent = message;
clone.querySelector('.toast-close').addEventListener('click', (e) => {
e.target.closest('.toast').remove();
});
document.getElementById('toast-container').appendChild(clone);
}
showToast('File saved successfully!');
</script>Common Mistakes
- Trying to query elements inside <template> with document.querySelector the content is in a DocumentFragment, not the main DOM
- Modifying the template itself instead of cloning first changes affect all future clones
- Forgetting to deep clone with cloneNode(true) shallow cloning omits child nodes
Note
Use <template> over innerHTML for repeated UI patterns it's faster (no HTML parsing on each clone), avoids XSS when combined with textContent/setAttribute (never innerHTML on user data), and clearly separates markup from logic.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Loading editor…
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4