How to Use HTML <template> & <slot> With Shadow DOM

Speed up your HTML templates with the slow tag and Shadow DOM. Learn how to optimize your website for faster loading times.

HTML Slot is one of the most remarkable standards made by W3C. Combine that with another impressive W3C standard called templates, and you have a fabulous concoction to work with. Being able to create and add HTML elements to a page using JavaScript is a necessary and important task.

It’s useful when a code snippet has to appear only at certain times, or when you don’t want to type out hundreds of similarly structured HTML elements but want to automize the process.

Creating HTML elements in JavaScript is not so desirable. It’s a hassle having to check and recheck if you have covered all the tags, placed them in the right order, all in all, there is just too much to type and keep track of. This turmoil, however, got a solution when the <template> tag appeared. If something needs to be added to the page dynamically, you can put it inside of the <template> element.

In this post, I will show you how you can use the <slot> and <template> tags together with JavaScript to create a mini HTML table factory that can create and populate hundreds of similar tables.

The <slot> and <template> tags

The <template> tag holds HTML code that won’t be rendered by browsers until it’s properly added to the document, using JavaScript. The <slot> tag is a placeholder you add to a Shadow DOM which can be made of the content of the <template> element.

A Shadow DOM is similar to a regular DOM (the document model parsed from HTML). It creates a scoped tree (a Shadow DOM tree), that has a root of its own and can also have a style of its own.

When you insert the Shadow DOM tree into an element in the main document — the element will then be called the shadow host —, all the child elements of the shadow host that are marked with the slot attribute (not the same as the aforementioned <slot> tag) will take their place in the newly inserted subtree.

Addition of the Shadow DOM
html-template-slow-tag-shadow-dom: W3C

The Shadow DOM, as of writing this article (July 2017), are supported only in WebKit- and Blink-based browsers but you can check the actual state of browser support on CanIUse at any time.

Setting up the HTML <template>

Still confusing? Let’s see some code, starting with the <template> element.

Inside <template>, there’s a <table> we’ll use as a blueprint for creating some tables that’ll be added to a document. There are <slot> elements inside the table cells (<th> and <td>) acting as placeholders for the column titles and cell values. Each slot has a unique name attribute that identifies it.

      <th><slot name='title-1'></slot></th>
      <th><slot name='title-2'></slot></th>
      <td><slot name='value-1.1'></slot></td>
      <td><slot name='value-1.2'></slot></td>
      <td><slot name='value-2.1'></slot></td>
      <td><slot name='value-2.2'></slot></td>
    table {
        table-layout: fixed;
        border-collapse: collapse;
        margin-bottom: 10px;
    th {
        width: 300px;
    td {
        border: 1px solid;

Inside the template, I’ve also added some basic styles for the table, using the <style> tag.

Outside the template, there are two <div> elements carrying the column titles and cell values inside <span>, for two separate tables we want to add to the page.

  <span slot='title-1'>Title A</span>
  <span slot='title-2'>Title B</span>
  <span slot='value-1.1'>Value A.1</span>
  <span slot='value-1.2'>Value A.2</span>
  <span slot='value-2.1'>Value B.1</span>
  <span slot='value-2.2'>Value B.2</span>

  <span slot='title-1'>Title C</span>
  <span slot='title-2'>Title D</span>
  <span slot='value-1.1'>Value C.1</span>
  <span slot='value-1.2'>Value C.2</span>
  <span slot='value-2.1'>Value D.1</span>
  <span slot='value-2.2'>Value D.2</span>

Each <span> element has a slot attribute of which value is equal to the name value of their corresponding <slot> tag inside <template>.

Right now, all you can see on the page are the text strings contained in the spans, so we need to add some JavaScript as well.

Attaching the Shadow DOM tree

Using Javascript, we insert the table from inside the template into both divs as a Shadow DOM tree. After the insertion, the spans get placed into their respective slots inside the table and display the desired column titles or cell values. The result will be two auto-generated tables that use the same template.

First, we need to check if the Shadow DOM is supported in the user’s browser. The attachShadow() method attaches a Shadow DOM tree to an element and returns the root node of that Shadow DOM tree. The if condition in the code below checks if the browser supports this method by testing if the divs on the page have the attachShadow method.

// check if Shadow DOM is supported
if ('attachShadow' in document.createElement('div')){

  console.warn('attachShadow not supported');

We create a custom variable called templateContent that serves as a reference to the content of the template.

if('attachShadow' in document.createElement('div')) {
  let templateContent = document.querySelector('template').content;
  let divs = document.querySelectorAll('div');

  divs.forEach(function(div) {
      // inside loop
  console.warn('attachShadow not supported');

Inside the forEach loop, a Shadow DOM tree is attached to each div (div.attachShadow({ mode: 'open' })).

There are two mode options for attachShadow: open and closed. If closed was chosen the root node of the Shadow DOM tree would become inaccessible to outside DOM elements and objects.

Then, we add a copy of the template content to the Shadow DOM tree using the templateContent.cloneNode(true) method.

if('attachShadow' in document.createElement('div')) {
  let templateContent = document.querySelector('template').content;
  let divs = document.querySelectorAll('div');

    div.attachShadow({  mode: 'open' }).appendChild(
  console.warn('attachShadow not supported');

And, our dynamic HTML tables are ready, here’s how the output looks like in Chrome:

Dynamic tables generated using HTML slots