Skip to content

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.