Learning HTML - HTML Templates and Basic Shadow DOM
Episode 18 of 23

Learning HTML - HTML Templates and Basic Shadow DOM

This episode breaks down templates and the shadow DOM: the template element for storing unrendered markup, slots for content distribution, and style and structure encapsulation through custom elements.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Complex pages demand markup fragments that get reused over and over. Episode 18 covers HTML templates and basic shadow DOM: the template element for storing unrendered markup, slot for content distribution, and the shadow DOM that isolates styles and structure from the main document.

These concepts are the foundation of Web Components — a way to create your own HTML elements that behave and style themselves independently. Understanding them lets you build truly reusable interfaces without depending on a framework.

The template Element

Markup That Isn't Rendered

template stores an HTML fragment that isn't displayed when the page loads. Its contents only become live when cloned and inserted via JavaScript:

HTMLThe template element
<template id="kartu-siswa">
  <div class="kartu">
    <h3></h3>
    <p></p>
  </div>
</template>

Content inside template is not rendered, loads no images, and can't be seen by users until it's used. It's a clean way to keep markup together with its document.

Cloning and Inserting Content

To use a template, access its content property, clone it with cloneNode(true), then fill and insert:

JSUsing a template
const template = document.getElementById("kartu-siswa");
const fragmen = template.content.cloneNode(true);
 
fragmen.querySelector("h3").textContent = "Ayu Lestari";
fragmen.querySelector("p").textContent = "Kelas 3A";
 
document.body.appendChild(fragmen);

The true argument on cloneNode ensures the entire DOM tree is copied, not just the outermost element. Each use produces a fresh copy, so the original markup stays intact.

Basic Shadow DOM

attachShadow and Open Mode

The shadow DOM is a separate DOM tree attached to a host element. Create it by calling attachShadow:

JSCreating a shadow root
const host = document.getElementById("widget-halo");
const shadow = host.attachShadow({ mode: "open" });
 
shadow.innerHTML = "<p>Halo dari dalam shadow DOM</p>";

The open mode makes the shadow root accessible from external JavaScript — try host.shadowRoot in the console to inspect its contents. The closed mode hides it from external access, but adds no real security; open is more common and easier to inspect.

Style Encapsulation

Parent page CSS rules don't penetrate the shadow root by default. Conversely, styles inside the shadow don't leak out — that's the essence of encapsulation:

JSStyles inside the shadow
shadow.innerHTML = `
  <style>
    p { color: #2563eb; font-weight: 700; }
  </style>
  <p>Halo dari dalam shadow DOM</p>
`;

The p inside the shadow isn't affected by the page's p { color: red } rule, and vice versa. This prevents style collisions in large projects.

Slots and Content Distribution

The shadow DOM doesn't have to be completely closed. The slot element opens holes that accept content from outside, directed by the slot attribute:

HTMLNamed slot
<my-kartu>
  <span slot="judul">Pengumuman</span>
  <span slot="tanggal">10 Agustus 2026</span>
</my-kartu>

Inside the shadow, slot name="judul" maps the span with the judul slot to its place. The original content stays in the light DOM and is styled by the parent page.

Your First Custom Element

Combine everything: a custom element that attaches a shadow root and renders its slots when created:

JSThe KartuKabar custom element
class KartuKabar extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: "open" });
    shadow.innerHTML = `
      <style>
        .kartu { border: 1px solid #d1d5db; padding: 1rem; }
        .judul { font-weight: 700; }
      </style>
      <div class="kartu">
        <div class="judul"><slot name="judul"></slot></div>
        <div><slot name="tanggal"></slot></div>
      </div>
    `;
  }
}
 
customElements.define("my-kartu", KartuKabar);

A custom element name must contain a hyphen, for example my-kartu, so it doesn't collide with built-in elements. After customElements.define, the <my-kartu> tag can be used anywhere on the page.

Exercise: A Card List from Data

Create several cards from an array of data using templates and the shadow DOM:

JSRendering many cards
const data = [
  { nama: "Ayu", kelas: "3A" },
  { nama: "Bima", kelas: "3B" },
  { nama: "Citra", kelas: "3C" },
];
 
for (const item of data) {
  const fragmen = template.content.cloneNode(true);
  fragmen.querySelector("h3").textContent = item.nama;
  fragmen.querySelector("p").textContent = "Kelas " + item.kelas;
  document.body.appendChild(fragmen);
}

Replace the contents of data with your real list. Because the template is cloned on each iteration, no cards share the same elements — safe for dynamic data.

Common Mistakes and Solutions

Inserting a Template Directly

Adding a template itself to the document displays nothing — it stays inert. Always clone its content with cloneNode(true) before inserting.

Searching for Elements Inside the Shadow

document.querySelector doesn't reach into the shadow root. To search it, access it via host.shadowRoot.querySelector(...) when the mode is open.

Closing

Episode 18 introduces how to build isolated components: template for stored markup, slot for external content, and the shadow DOM for style and structure encapsulation through custom elements.

Key takeaways:

  • template stores markup that isn't rendered until cloned.
  • cloneNode(true) creates a complete copy of the DOM tree.
  • attachShadow creates a separate DOM tree on a host element.
  • Styles don't cross the shadow root boundary in either direction.
  • slot receives content from the light DOM via the slot attribute.
  • Custom element names must contain a hyphen.

In the next episode, episode 19, we'll cover practices for writing clean, maintainable HTML — indentation, heading hierarchy, class naming, and automated linting so your markup is comfortable for your team to read.

Learning HTML - HTML Templates and Basic Shadow DOM | Learning HTML