Templates, Slots and Custom Elements
Write a layout once with a template and named slots, give a card its own layout with declarative shadow DOM and no JavaScript, and use custom elements such as a star rating from a ready-made script.
Freelancer · HTML · Updated
After this lesson you can
- Explain what a template element holds and why the browser doesn't show it
- Build a card layout with declarative shadow DOM and named slots
- Use a custom element from a ready-made script, passing it attributes and content
Key takeaways
- A
<template>holds HTML the browser reads but doesn't show. Its pictures don't load and its scripts don't run until something uses it. - A
<slot>is a hole in a layout. Content withslot="title"fills the slot named title, and content without a slot attribute fills the slot with no name. - Declarative shadow DOM, a
<template shadowrootmode="open">inside an element, gives that element a layout of its own with no JavaScript. It works in every current browser. - A custom element is a tag you or a library makes up, with a hyphen in its name, such as
<star-rating>. A script says what it does; your HTML gives it attributes and content. - Put readable content inside a custom element, such as "4 out of 5 stars", so the page still makes sense before the script loads.
Pixel Palace, a made-up video game store, posts player reviews: a cover picture, the game's title, a star rating, the reviewer's handle and what they thought. There are forty of them. When the owner wants the stars moved above the title, someone edits forty copies of the same layout, and misses two. Real sites solve this with reusable HTML: the layout is written once, and each review fills it in. GitHub's pages use custom elements such as <relative-time> to show "3 hours ago" next to every comment, and Google's <model-viewer> puts a 3D model you can spin with your finger into a page with one tag. This lesson shows the HTML side of how that works: templates, slots, shadow DOM and custom elements.
A template: HTML kept in a drawer
A cookie cutter isn't a cookie. It sits in the drawer, holding a shape, until you use it to make cookies. A <template> is HTML in the drawer: the browser reads it, but doesn't show it.
<template id="review-card">
<article>
<img src="covers/moon-garden.svg" alt="">
<h3>Game title goes here</h3>
</article>
</template>
Put this on a page and nothing appears. The browser parses what's inside into a separate piece of document called the template's contents, and leaves it inert: its pictures aren't downloaded, its scripts don't run, and a screen reader doesn't read it. In the Elements panel it shows as #document-fragment under the <template>. A template waits for something to use it, usually a script that copies it into the page as many times as it needs.
Slots: holes in the layout
A fill-in-the-blank form letter has the same wording every time, with blanks for the name and the date. A <slot> is a blank in a layout. A named slot has a name, and content says which blank it fills with the slot attribute:
<!-- In the layout: -->
<h3><slot name="title">A game with no name</slot></h3>
<slot name="review"></slot>
<!-- In the content: -->
<span slot="title">Moon Garden</span>
<p slot="review">The calmest game I own. I water my space plants every night.</p>
slot="title"on the content matches<slot name="title">in the layout. The names must match exactly, capital letters included.- Text inside a
<slot>is its fallback: it shows only when nothing fills that slot. - A slot with no
nameis the default slot. Everything that has noslotattribute goes there.
Slots work inside a shadow root, which is the next section.
Declarative shadow DOM: a layout of its own, with no JavaScript
Think of a picture frame with a mat: the frame decides the layout, and you slide your own photo in behind it. Your photo stays yours; the frame is the frame's.
Declarative shadow DOM gives any <div>, <section>, <article> or custom element a frame like that. Put a <template shadowrootmode="open"> inside it, and the parser turns the template into a shadow root: a hidden layout attached to that element, which is now called the shadow host. The host's own children (the photo) are shown through the slots of the layout (the frame):
<article class="review">
<template shadowrootmode="open">
<link rel="stylesheet" href="review-card.css">
<header>
<slot name="cover"></slot>
<h3><slot name="title"></slot></h3>
<slot name="rating"></slot>
</header>
<slot name="review"></slot>
</template>
<img slot="cover" src="covers/turbo-kart-rally.svg" alt="Turbo Kart Rally cover: a red go-kart drifting around a corner of a candy-striped track" width="120" height="160">
<span slot="title">Turbo Kart Rally</span>
<span slot="rating">4 out of 5 stars</span>
<p slot="review">Drift through the corners and you get a speed boost. My little brother beats me every time.</p>
</article>
The page shows a card with the cover, title and rating in a row and the review below, in the order the layout says, whatever order the content is in. "open" means the shadow root can be reached in the developer tools and by scripts; it's the one to use.
Three things to know:
- It's built by the parser, so it needs no script. The card has its layout the moment the HTML arrives. Declarative shadow DOM is Baseline widely available: it has worked in Chrome, Edge and Safari since 2023 and in Firefox since February 2024 (checked on MDN on 2026-10-28).
- Styles stay inside. A stylesheet linked inside the shadow root styles only the layout, and the page's stylesheet doesn't reach into it. The slotted content (the photo) still belongs to the page, so the page's styles apply to it.
- Each host carries its own copy. A declarative shadow root belongs to the one element it sits in. Four cards written this way need four
<template shadowrootmode>s. Real sites have a server or a build tool write those copies, or let a custom element make them, as the last section shows.
Try it. Make a file named card.html, add the page skeleton, and paste the card above into the <body> (leave out the <link> line; the card works without it). Open it with Live Server. Now move the <p slot="review"> above the <img> in your file and reload: the card looks the same, because the layout decides where each slot goes. Open the Elements panel and expand #shadow-root (open) under the <article>.
Custom elements: tags with a hyphen
HTML has about 110 elements, and people keep needing ones it doesn't have: a star rating, a "3 hours ago" time, a 3D model. A custom element is a tag a developer makes up, and a script teaches the browser what it does.
<script src="star-rating.js" defer></script>
<star-rating rating="4">4 out of 5 stars</star-rating>
- The name must have a hyphen (
-):star-rating,review-card,relative-time. HTML's own elements never will, so a custom element can't clash with a tag HTML adds next year. - Attributes go in.
rating="4"is how the page tells this element what to show. The script's documentation (or its README) lists the attributes it understands. - Content goes in too, through the element's slots, or as plain content like "4 out of 5 stars".
- Write a real end tag. A custom element is never a void element, so
<star-rating rating="4">always needs its</star-rating>.
Before the script has loaded, the browser treats an unknown element with a hyphen like a <span> with no meaning: it shows whatever is inside. That's why the words "4 out of 5 stars" are there. When star-rating.js runs, it replaces them with five stars and a label a screen reader reads aloud. With no script at all, the page still says something true.
If you want to write custom elements of your own, the JavaScript in Depth course teaches the script side. This course stays with the HTML side: tags, attributes and slots.
Written once, used many times
Put the pieces together. A ready-made script, review-card.js, defines a <review-card> element. All it does is copy the page's one <template id="review-card"> into a shadow root for each card. The layout lives in one place, and every card fills its slots:
<template id="review-card">
<link rel="stylesheet" href="review-card.css">
<article>
<header>
<h3><slot name="title"></slot></h3>
<slot name="rating"></slot>
</header>
<slot name="review"></slot>
</article>
</template>
<review-card>
<span slot="title">Moon Garden</span>
<star-rating slot="rating" rating="5">5 out of 5 stars</star-rating>
<p slot="review">The calmest game I own.</p>
</review-card>
<review-card>
<span slot="title">Turbo Kart Rally</span>
<star-rating slot="rating" rating="4">4 out of 5 stars</star-rating>
<p slot="review">My little brother beats me every time.</p>
</review-card>
Move the rating above the title in the template, and forty cards change at once. A template, slots, shadow DOM and custom elements used together are what developers call web components, and they work in every current browser with no framework.
Check yourself
2 questions to try on the lesson page.
So what
Every big site has a card, a button or a badge it repeats hundreds of times. With a template and slots, the layout is written once and the content stays plain, readable HTML, which is how design systems at large companies are built.