Docs

25.3-HTML-Templates

25.3 HTML Templates

Overview

HTML Templates provide a way to define reusable markup that is not rendered until activated. The <template> element holds content that can be cloned and inserted into the DOM.

Learning Objectives

  • Understand the <template> element
  • Clone and activate template content
  • Use templates with Web Components
  • Work with slots for content projection
  • Build reusable template patterns

The Template Element

Basic Template

<!-- Template is not rendered -->
<template id="my-template">
  <style>
    .card {
      padding: 20px;
      border: 1px solid #ccc;
    }
  </style>
  <div class="card">
    <h2>Template Content</h2>
    <p>This won't appear until activated</p>
  </div>
</template>

Template Properties

const template = document.getElementById('my-template');

// Check if it's a template
console.log(template instanceof HTMLTemplateElement); // true

// Access the content (DocumentFragment)
console.log(template.content);

// Template content is not part of the document
console.log(template.content.children); // HTMLCollection

Using Templates

Cloning and Inserting

const template = document.getElementById('my-template');

// Clone the template content (deep clone)
const clone = template.content.cloneNode(true);

// Modify the clone before inserting
clone.querySelector('h2').textContent = 'New Title';

// Insert into the document
document.body.appendChild(clone);

Multiple Instances

const template = document.getElementById('card-template');
const container = document.getElementById('container');

const items = ['Item 1', 'Item 2', 'Item 3'];

items.forEach((item) => {
  const clone = template.content.cloneNode(true);
  clone.querySelector('.title').textContent = item;
  container.appendChild(clone);
});

Templates with Web Components

Using Templates in Custom Elements

// Define template
const template = document.createElement('template');
template.innerHTML = `
    <style>
        :host {
            display: block;
            padding: 16px;
        }
        .container {
            background: #f5f5f5;
            border-radius: 8px;
            padding: 20px;
        }
    </style>
    <div class="container">
        <slot></slot>
    </div>
`;

class MyComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

customElements.define('my-component', MyComponent);

Inline Template Reference

<template id="user-card-template">
  <style>
    .user-card {
      display: flex;
      align-items: center;
      gap: 12px;
    }
    .avatar {
      width: 48px;
      height: 48px;
      border-radius: 50%;
    }
  </style>
  <div class="user-card">
    <img class="avatar" src="" alt="" />
    <div class="info">
      <h3 class="name"></h3>
      <p class="email"></p>
    </div>
  </div>
</template>

<script>
  class UserCard extends HTMLElement {
    connectedCallback() {
      const template = document.getElementById('user-card-template');
      const shadow = this.attachShadow({ mode: 'open' });
      shadow.appendChild(template.content.cloneNode(true));

      // Populate from attributes
      shadow.querySelector('.avatar').src = this.getAttribute('avatar');
      shadow.querySelector('.name').textContent = this.getAttribute('name');
      shadow.querySelector('.email').textContent = this.getAttribute('email');
    }
  }
  customElements.define('user-card', UserCard);
</script>

Slots in Templates

Default Slot

<template id="card-template">
  <div class="card">
    <slot>Default content</slot>
  </div>
</template>

Named Slots

<template id="article-template">
  <article>
    <header>
      <slot name="title">Untitled</slot>
    </header>
    <main>
      <slot>No content</slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </article>
</template>
<my-article>
  <h1 slot="title">Article Title</h1>
  <p>Main content paragraph</p>
  <span slot="footer">Posted today</span>
</my-article>

Slot Fallback Content

const template = document.createElement('template');
template.innerHTML = `
    <div class="notification">
        <span class="icon">
            <slot name="icon">ℹ️</slot>
        </span>
        <div class="content">
            <slot>Default message</slot>
        </div>
    </div>
`;

Dynamic Templates

Creating Templates Programmatically

function createTemplate(html) {
  const template = document.createElement('template');
  template.innerHTML = html;
  return template;
}

const cardTemplate = createTemplate(`
    <div class="card">
        <h3 class="title"></h3>
        <p class="description"></p>
    </div>
`);

Template Factory Pattern

class TemplateFactory {
  static templates = new Map();

  static register(name, html) {
    const template = document.createElement('template');
    template.innerHTML = html;
    this.templates.set(name, template);
    return template;
  }

  static get(name) {
    return this.templates.get(name);
  }

  static create(name, data = {}) {
    const template = this.get(name);
    if (!template) throw new Error(`Template ${name} not found`);

    const clone = template.content.cloneNode(true);

    // Apply data to clone
    for (const [key, value] of Object.entries(data)) {
      const element = clone.querySelector(`[data-bind="${key}"]`);
      if (element) {
        element.textContent = value;
      }
    }

    return clone;
  }
}

// Usage
TemplateFactory.register(
  'user',
  `
    <div class="user">
        <span data-bind="name"></span>
        <span data-bind="email"></span>
    </div>
`
);

const userElement = TemplateFactory.create('user', {
  name: 'John Doe',
  email: 'john@example.com',
});

Template Best Practices

  1. Cache templates - Don't query for templates repeatedly
  2. Clone deeply - Always use cloneNode(true)
  3. Modify before inserting - Update clones before adding to DOM
  4. Use slots for composition - Let consumers provide content
  5. Keep templates simple - Complex logic belongs in JavaScript

Browser Support

// Check for template support
if ('content' in document.createElement('template')) {
  console.log('Templates supported');
} else {
  console.log('Templates not supported - use polyfill');
}

Summary

FeatureDescription
<template>Container for inactive markup
.contentDocumentFragment with template contents
.cloneNode(true)Create deep copy of template
<slot>Placeholder for projected content
slot="name"Target specific named slot

Resources

.3 HTML Templates - JavaScript Tutorial | DeepML