Skip to content

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.