A message list needs to add a new card for every incoming message, all identical in structure, only the content changing. Building that structure entirely in JavaScript, with repeated calls to createElement, works but quickly becomes hard to read and maintain once the card holds several nested levels. It would be simpler to write the blueprint once in HTML, then duplicate it as many times as needed.
That is exactly the role of the <template> tag: holding a fragment of HTML ready to be cloned, without ever being rendered as is.
Definition
<template> declares an inert fragment of HTML content. The browser parses that content to check it is syntactically valid, but never displays it, never runs any script it contains, never loads any image, and never lets it take part in the page's styles or layout measurements. That content only becomes usable once explicitly cloned with JavaScript.
This inertness is the whole point of the tag: it allows a complex blueprint to be written ahead of time, with no side effects, ready to be duplicated whenever needed.
Cloning the content in JavaScript
The content property of a <template> element gives access to a DocumentFragment holding the blueprint. The cloneNode(true) method produces a full copy of it, ready to be inserted into the active document.
<template id="message-card">
<article class="message">
<strong class="author"></strong>
<p class="text"></p>
</article>
</template>const template = document.getElementById('message-card');
const clone = template.content.cloneNode(true);
clone.querySelector('.author').textContent = 'Camille';
clone.querySelector('.text').textContent = 'Message received.';
document.querySelector('.list').appendChild(clone);Each call to cloneNode(true) produces an independent copy: editing one never affects the others, nor the original blueprint, which stays unchanged inside the <template> tag.
Difference with a display none div
A <div style="display:none"> stays a fully active element of the DOM, simply invisible on screen. Scripts it contains still run, its images still load, its styles still get computed, and a poorly configured screen reader can sometimes even announce it. The content of a <template>, on the other hand, is not even present in the page's active DOM at all: it lives in a separate document, invisible to ordinary CSS selectors and absent from any normal DOM traversal until it has been cloned.
This difference has concrete consequences. An <img> tag placed inside a hidden <div> still downloads its image in the background, wasting bandwidth on invisible content. The same image placed inside a <template> triggers no download at all until the fragment has been cloned and inserted.
Pairing with <slot>
<slot> makes full sense inside a <template> used to define a custom element: the template describes the internal structure cloned into the shadow DOM, and the spots marked <slot> indicate where content supplied by whoever uses the component should be inserted once that cloning happens.
Frequently asked questions
template reachable with querySelector on the document?No, not until it has been cloned and inserted into the active document. A call to document.querySelector('.author') finds nothing inside an uncloned <template>, since that content lives in a separate document, apart from the page's main DOM.
template hold several root elements?Yes, unlike many frameworks that require a single root element for a component, a <template> accepts as many sibling nodes as needed at its top level, exactly like any other fragment of HTML.
template be used for content shown only once when the page loads?No, if the content needs to appear only once and stay static, writing it directly into the page's HTML stays simpler and faster. The value of <template> shows up when the same structure needs to be duplicated several times, or cloned in response to an event.