<template> HTML content template element

Baseline Widely available *

This feature is well established and works across many devices and browser versions. It’s been available across browsers since November 2015.

* Some parts of this feature may have varying levels of support.

The <template> HTML element serves as a mechanism for holding HTML fragments, which can either be used later via JavaScript or generated immediately into shadow DOM.

Attributes

This element includes the global attributes.

shadowrootmode

Creates a shadow root for the parent element. It is a declarative version of the Element.attachShadow() method and accepts the same enumerated values.

open

Exposes the internal shadow root DOM for JavaScript (recommended for most use cases).

closed

Hides the internal shadow root DOM from JavaScript.

Note: The HTML parser creates a ShadowRoot object in the DOM for the first <template> in a node with this attribute set to an allowed value. If the attribute is not set, or not set to an allowed value — or if a ShadowRoot has already been declaratively created in the same parent — then an HTMLTemplateElement is constructed. A HTMLTemplateElement cannot subsequently be changed into a shadow root after parsing, for example, by setting HTMLTemplateElement.shadowRootMode.

Note: You may find the non-standard shadowroot attribute in older tutorials and examples that used to be supported in Chrome 90-110. This attribute has since been removed and replaced by the standard shadowrootmode attribute.

shadowrootclonable

Sets the value of the clonable property of a ShadowRoot created using this element to true. If set, a clone of the shadow host (the parent element of this <template>) created with Node.cloneNode() or Document.importNode() will include a shadow root in the copy.

shadowrootcustomelementregistry

Sets the customElementRegistry property of a ShadowRoot created using this element to null, rather than the document's custom element registry. This allows a scoped CustomElementRegistry to be attached later using CustomElementRegistry.initialize().

shadowrootdelegatesfocus

Sets the value of the delegatesFocus property of a ShadowRoot created using this element to true. If this is set and a non-focusable element in the shadow tree is selected, then focus is delegated to the first focusable element in the tree. The value defaults to false.

shadowrootreferencetarget

Sets the value of the referenceTarget property of a ShadowRoot created using this element. The value should be the ID of an element inside the shadow DOM. If set, target references to the host element from outside the shadow DOM will cause the referenced target element to become the effective target of the reference to the host element.

shadowrootserializable

Sets the value of the serializable property of a ShadowRoot created using this element to true. If set, the shadow root may be serialized by calling the Element.getHTML() or ShadowRoot.getHTML() methods with the options.serializableShadowRoots parameter set true. The value defaults to false.

Usage notes

This element has no permitted content, because everything nested inside it in the HTML source does not actually become the children of the <template> element. The Node.childNodes property of the <template> element is always empty, and you can only access said nested content via the special content property. However, if you call Node.appendChild() or similar methods on the <template> element, then you would be inserting children into the <template> element itself, which is a violation of its content model and does not actually update the DocumentFragment returned by the content property.

Due to the way the <template> element is parsed, all <html>, <head>, and <body> opening and closing tags inside the template are syntax errors and are ignored by the parser, so <template><head><title>Test</title></head></template> is the same as <template><title>Test</title></template>.

There are two main ways to use the <template> element.

Template document fragment

By default, the element's content is not rendered. The corresponding HTMLTemplateElement interface includes a standard content property (without an equivalent content/markup attribute). This content property is read-only and holds a DocumentFragment that contains the DOM subtree represented by the template.

The Node.cloneNode() and Document.importNode() methods both create a copy of a node. The difference is that importNode() clones the node in the context of the calling document, whereas cloneNode() uses the document of the node being cloned. The document context determines the CustomElementRegistry for constructing any custom elements. For this reason, use document.importNode() to clone the content fragment so that custom element descendants are constructed using the definitions in the current document, rather than the separate document that owns the template content. See the Node.cloneNode() page's examples for more details.

Note that the DocumentFragment container itself should not have data attached to it. See the Data on the DocumentFragment is not cloned example for more details.

Declarative Shadow DOM

If the <template> element contains the shadowrootmode attribute with a value of either open or closed, the HTML parser will immediately generate a shadow DOM. The element is replaced in the DOM by its content wrapped in a ShadowRoot, which is attached to the parent element. This is the declarative equivalent of calling Element.attachShadow() to attach a shadow root to an element.

If the element has any other value for shadowrootmode, or does not have the shadowrootmode attribute, the parser generates a HTMLTemplateElement. Similarly, if there are multiple declarative shadow roots, only the first one is replaced by a ShadowRoot — subsequent instances are parsed as HTMLTemplateElement objects.

Examples

Generating table rows

First we start with the HTML portion of the example.

html
<table id="producttable">
  <thead>
    <tr>
      <td>UPC_Code</td>
      <td>Product_Name</td>
    </tr>
  </thead>
  <tbody>
    <!-- existing data could optionally be included here -->
  </tbody>
</table>

<template id="productrow">
  <tr>
    <td class="record"></td>
    <td></td>
  </tr>
</template>

First, we have a table into which we will later insert content using JavaScript code. Then comes the template, which describes the structure of an HTML fragment representing a single table row.

Now that the table has been created and the template defined, we use JavaScript to insert rows into the table, with each row being constructed using the template as its basis.

js
// Test to see if the browser supports the HTML template element by checking
// for the presence of the template element's content attribute.
if ("content" in document.createElement("template")) {
  // Instantiate the table with the existing HTML tbody
  // and the row with the template
  const tbody = document.querySelector("tbody");
  const template = document.querySelector("#productrow");

  // Clone the new row and insert it into the table
  const clone = document.importNode(template.content, true);
  let td = clone.querySelectorAll("td");
  td[0].textContent = "1235646565";
  td[1].textContent = "Stuff";

  tbody.appendChild(clone);

  // Clone the new row and insert it into the table
  const clone2 = document.importNode(template.content, true);
  td = clone2.querySelectorAll("td");
  td[0].textContent = "0384928528";
  td[1].textContent = "Acme Kidney Beans 2";

  tbody.appendChild(clone2);
} else {
  // Find another way to add the rows to the table because
  // the HTML template element is not supported.
}

The result is the original HTML table, with two new rows appended to it via JavaScript:

Implementing a declarative shadow DOM

In this example, a hidden support warning is included at the beginning of the markup. This warning is later set to be displayed via JavaScript if the browser doesn't support the shadowrootmode attribute. Next, there are two <article> elements, each containing nested <style> elements with different behaviors. The first <style> element is global to the whole document. The second one is scoped to the shadow root generated in place of the <template> element because of the presence of the shadowrootmode attribute.

html
<p hidden>
  ⛔ Your browser doesn't support <code>shadowrootmode</code> attribute yet.
</p>
<article>
  <style>
    p {
      padding: 8px;
      background-color: wheat;
    }
  </style>
  <p>I'm in the DOM.</p>
</article>
<article>
  <template shadowrootmode="open">
    <style>
      p {
        padding: 8px;
        background-color: plum;
      }
    </style>
    <p>I'm in the shadow DOM.</p>
  </template>
</article>
js
const isShadowRootModeSupported = Object.hasOwn(
  HTMLTemplateElement.prototype,
  "shadowRootMode",
);

document
  .querySelector("p[hidden]")
  .toggleAttribute("hidden", isShadowRootModeSupported);

Declarative Shadow DOM with delegated focus

This example demonstrates how shadowrootdelegatesfocus is applied to a shadow root that is created declaratively, and the effect this has on focus.

The code first declares a shadow root inside a <div> element, using the <template> element with the shadowrootmode attribute. This displays both a non-focusable <div> containing text and a focusable <input> element. It also uses CSS to style elements with :focus to blue, and to set the normal styling of the host element.

html
<div>
  <template shadowrootmode="open">
    <style>
      :host {
        display: block;
        border: 1px dotted black;
        padding: 10px;
        margin: 10px;
      }
      :focus {
        outline: 2px solid blue;
      }
    </style>
    <div>Clickable Shadow DOM text</div>
    <input type="text" placeholder="Input inside Shadow DOM" />
  </template>
</div>

The second code block is identical except that it sets the shadowrootdelegatesfocus attribute, which delegates focus to the first focusable element in the tree if a non-focusable element in the tree is selected.

html
<div>
  <template shadowrootmode="open" shadowrootdelegatesfocus>
    <style>
      :host {
        display: block;
        border: 1px dotted black;
        padding: 10px;
        margin: 10px;
      }
      :focus {
        outline: 2px solid blue;
      }
    </style>
    <div>Clickable Shadow DOM text</div>
    <input type="text" placeholder="Input inside Shadow DOM" />
  </template>
</div>

Last of all we use the following CSS to apply a red border to the parent <div> element when it has focus.

css
div:focus {
  border: 2px solid red;
}

The results are shown below. When the HTML is first rendered, the elements have no styling, as shown in the first image. For the shadow root that does not have shadowrootdelegatesfocus set you can click anywhere except the <input> and the focus does not change (if you select the <input> element it will look like the second image).

Screenshot of code with no focus set

For the shadow root with shadowrootdelegatesfocus set, clicking on the text (which is non-focusable) selects the <input> element, as this is the first focusable element in the tree. This also focuses the parent element as shown below.

Screenshot of the code where the element has focus

Data on the DocumentFragment is not cloned

When a DocumentFragment value is passed, Node.appendChild and similar methods move only the child nodes of that value into the target node. Therefore, it is usually preferable to attach event handlers to the children of a DocumentFragment, rather than to the DocumentFragment itself.

Consider the following HTML and JavaScript:

HTML

html
<div id="container"></div>

<template id="template">
  <div>Click me</div>
</template>

JavaScript

js
const container = document.getElementById("container");
const template = document.getElementById("template");

function clickHandler(event) {
  event.target.append(" — Clicked this div");
}

const firstClone = document.importNode(template.content, true);
firstClone.addEventListener("click", clickHandler);
container.appendChild(firstClone);

const secondClone = document.importNode(template.content, true);
secondClone.children[0].addEventListener("click", clickHandler);
container.appendChild(secondClone);

Result

Since firstClone is a DocumentFragment, only its children are added to container when appendChild is called; the event handlers of firstClone are not copied. In contrast, because an event handler is added to the first child node of secondClone, the event handler is copied when appendChild is called, and clicking on it works as one would expect.

Technical summary

Content categories Metadata content, flow content, phrasing content, script-supporting element
Permitted content Nothing (see Usage notes)
Tag omission None, both the starting and ending tag are mandatory.
Permitted parents Any element that accepts metadata content, phrasing content, or script-supporting elements. Also allowed as a child of a <colgroup> element that does not have a span attribute.
Implicit ARIA role No corresponding role
Permitted ARIA roles No role permitted
DOM interface HTMLTemplateElement

Specifications

Specification
HTML
# the-template-element

Browser compatibility

desktop mobile
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
template
Chrome – Full support
Chrome 26 (Release date: 2013-03-26)
footnote Full support
Edge – Full support
Edge 13 (Release date: 2015-11-12)
footnote Full support
Firefox – Full support
Firefox 22 (Release date: 2013-06-25)
footnote Full support
Opera – Full support
Opera 15 (Release date: 2013-07-02)
footnote Full support
Safari – Full support
Safari 8 (Release date: 2014-10-16)
footnote Full support
Chrome Android – Full support
Chrome Android 26 (Release date: 2013-04-03)
footnote Full support
Firefox for Android – Full support
Firefox for Android 22 (Release date: 2013-06-25)
footnote Full support
Opera Android – Full support
Opera Android 14 (Release date: 2013-05-21)
footnote Full support
Safari on iOS – Full support
Safari on iOS 8 (Release date: 2014-09-17)
footnote Full support
Samsung Internet – Full support
Samsung Internet 1.5 (Release date: 2013-09-25)
footnote Full support
WebView Android – Full support
WebView Android 4.4 (Release date: 2013-12-09)
footnote Full support
WebView on iOS – Full support
WebView on iOS 8 (Release date: 2014-09-17)
footnote Full support
shadowrootclonable
Chrome – Full support
Chrome 124 (Release date: 2024-04-16)
footnote Full support
Edge – Full support
Edge 124 (Release date: 2024-04-18)
footnote Full support
Firefox – Full support
Firefox 125 (Release date: 2024-04-16)
footnote Full support
Opera – Full support
Opera 110 (Release date: 2024-05-14)
footnote Full support
Safari – Full support
Safari 17.5 (Release date: 2024-05-13)
footnote Full support
Chrome Android – Full support
Chrome Android 124 (Release date: 2024-04-16)
footnote Full support
Firefox for Android – Full support
Firefox for Android 125 (Release date: 2024-04-16)
footnote Full support
Opera Android – Full support
Opera Android 82 (Release date: 2024-05-02)
footnote Full support
Safari on iOS – Full support
Safari on iOS 17.5 (Release date: 2024-05-13)
footnote Full support
Samsung Internet – Full support
Samsung Internet 27 (Release date: 2024-11-06)
footnote Full support
WebView Android – Full support
WebView Android 124 (Release date: 2024-04-16)
footnote Full support
WebView on iOS – Full support
WebView on iOS 17.5 (Release date: 2024-05-13)
footnote Full support
shadowrootcustomelementregistry
Chrome – Full support
Chrome 146 (Release date: 2026-03-10)
footnote Full support
Edge – Full support
Edge 146 (Release date: 2026-03-13)
footnote Full support
Firefox – No support
Firefox
footnote No support
Opera – Full support
Opera 130 (Release date: 2026-04-08)
footnote Full support
Safari – Full support
Safari 26 (Release date: 2025-09-15)
footnote Full support
Chrome Android – Full support
Chrome Android 146 (Release date: 2026-03-10)
footnote Full support
Firefox for Android – No support
Firefox for Android
footnote No support
Opera Android – Full support
Opera Android 97 (Release date: 2026-04-16)
footnote Full support
Safari on iOS – Full support
Safari on iOS 26 (Release date: 2025-09-15)
footnote Full support
Samsung Internet – No support
Samsung Internet
footnote No support
WebView Android – Full support
WebView Android 146 (Release date: 2026-03-10)
footnote Full support
WebView on iOS – Full support
WebView on iOS 26 (Release date: 2025-09-15)
footnote Full support
shadowrootdelegatesfocus
Chrome – Full support
Chrome 123 (Release date: 2024-03-19)
footnote Full support
Edge – Full support
Edge 123 (Release date: 2024-03-22)
footnote Full support
Firefox – Full support
Firefox 123 (Release date: 2024-02-20)
footnote Full support
Opera – Full support
Opera 109 (Release date: 2024-03-27)
footnote Full support
Safari – Full support
Safari 16.4 (Release date: 2023-03-27)
footnote Full support
Chrome Android – Full support
Chrome Android 123 (Release date: 2024-03-19)
footnote Full support
Firefox for Android – Full support
Firefox for Android 123 (Release date: 2024-02-20)
footnote Full support
Opera Android – Full support
Opera Android 82 (Release date: 2024-05-02)
footnote Full support
Safari on iOS – Full support
Safari on iOS 16.4 (Release date: 2023-03-27)
footnote Full support
Samsung Internet – Full support
Samsung Internet 27 (Release date: 2024-11-06)
footnote Full support
WebView Android – Full support
WebView Android 123 (Release date: 2024-03-19)
footnote Full support
WebView on iOS – Full support
WebView on iOS 16.4 (Release date: 2023-03-27)
footnote Full support
shadowrootmode
Chrome – No support
Chrome 90 – 110 (Release date: 2021-04-13)
altname
altname Alternate name: shadowroot
Chrome – Full support
Chrome 111 (Release date: 2023-03-07)
footnote Full support
Edge – No support
Edge 90 – 110 (Release date: 2021-04-15)
altname
altname Alternate name: shadowroot
Edge – Full support
Edge 111 (Release date: 2023-03-13)
footnote Full support
Firefox – Full support
Firefox 123 (Release date: 2024-02-20)
footnote Full support
Opera – No support
Opera 76 – 96 (Release date: 2021-04-28)
altname
altname Alternate name: shadowroot
Opera – Full support
Opera 97 (Release date: 2023-03-22)
footnote Full support
Safari – Full support
Safari 16.4 (Release date: 2023-03-27)
footnote Full support
Chrome Android – No support
Chrome Android 90 – 110 (Release date: 2021-04-13)
altname
altname Alternate name: shadowroot
Chrome Android – Full support
Chrome Android 111 (Release date: 2023-03-07)
footnote Full support
Firefox for Android – Full support
Firefox for Android 123 (Release date: 2024-02-20)
footnote Full support
Opera Android – No support
Opera Android 64 – 74 (Release date: 2021-05-25)
altname
altname Alternate name: shadowroot
Opera Android – Full support
Opera Android 75 (Release date: 2023-05-17)
footnote Full support
Safari on iOS – Full support
Safari on iOS 16.4 (Release date: 2023-03-27)
footnote Full support
Samsung Internet – No support
Samsung Internet 15 – 21 (Release date: 2021-08-13)
altname
altname Alternate name: shadowroot
Samsung Internet – Full support
Samsung Internet 22 (Release date: 2023-07-14)
footnote Full support
WebView Android – No support
WebView Android 90 – 110 (Release date: 2021-04-13)
altname
altname Alternate name: shadowroot
WebView Android – Full support
WebView Android 111 (Release date: 2023-03-01)
footnote Full support
WebView on iOS – Full support
WebView on iOS 16.4 (Release date: 2023-03-27)
footnote Full support
shadowrootreferencetarget
Experimental Non-standard
Chrome – No support
Chrome 133 (Release date: 2025-02-04)
disabled
disabled From version 133 users must explicitly set the #enable-experimental-web-platform-features preference to true. To change preferences in Chrome, visit chrome://flags.
Edge – No support
Edge 133 (Release date: 2025-02-06)
disabled
disabled From version 133 users must explicitly set the #enable-experimental-web-platform-features preference to true. To change preferences in Edge, visit about:flags.
Firefox – No support
Firefox 144 (Release date: 2025-10-14)
disabled
disabled From version 144 users must explicitly set the dom.shadowdom.referenceTarget.enabled preference to true. To change preferences in Firefox, visit about:config.
Opera – No support
Opera 118 (Release date: 2025-04-15)
disabled
disabled From version 118 users must explicitly set the #enable-experimental-web-platform-features preference to true. To change preferences in Opera, visit opera://flags.
Safari – No support
Safari 26 (Release date: 2025-09-15)
disabled
disabled From version 26 users must explicitly set the referenceTarget preference to true.
disabled From version 26 users must explicitly set the referenceTarget support for aria-owns preference to true.
Chrome Android – No support
Chrome Android 133 (Release date: 2025-02-04)
disabled
disabled From version 133 users must explicitly set the #enable-experimental-web-platform-features preference to true. To change preferences in Chrome Android, visit chrome://flags.
Firefox for Android – No support
Firefox for Android
footnote No support
Opera Android – No support
Opera Android
footnote No support
Safari on iOS – No support
Safari on iOS 26 (Release date: 2025-09-15)
disabled
disabled From version 26 users must explicitly set the referenceTarget preference to true.
disabled From version 26 users must explicitly set the referenceTarget support for aria-owns preference to true.
Samsung Internet – No support
Samsung Internet
footnote No support
WebView Android – No support
WebView Android
footnote No support
WebView on iOS – No support
WebView on iOS
footnote No support
shadowrootserializable
Chrome – Full support
Chrome 125 (Release date: 2024-05-14)
footnote Full support
Edge – Full support
Edge 125 (Release date: 2024-05-17)
footnote Full support
Firefox – No support
Firefox
footnote No support
Opera – Full support
Opera 111 (Release date: 2024-06-12)
footnote Full support
Safari – Full support
Safari 18 (Release date: 2024-09-16)
footnote Full support
Chrome Android – Full support
Chrome Android 125 (Release date: 2024-05-14)
footnote Full support
Firefox for Android – No support
Firefox for Android
footnote No support
Opera Android – Full support
Opera Android 83 (Release date: 2024-06-25)
footnote Full support
Safari on iOS – Full support
Safari on iOS 18 (Release date: 2024-09-16)
footnote Full support
Samsung Internet – Full support
Samsung Internet 27 (Release date: 2024-11-06)
footnote Full support
WebView Android – Full support
WebView Android 125 (Release date: 2024-05-14)
footnote Full support
WebView on iOS – Full support
WebView on iOS 18 (Release date: 2024-09-16)
footnote Full support
shadowrootslotassignment
Experimental
Chrome – No support
Chrome
footnote No support
Edge – No support
Edge
footnote No support
Firefox – Preview support
Firefox 151 (Release date: 2026-05-19)
footnote Preview browser support
Opera – No support
Opera
footnote No support
Safari – No support
Safari
footnote No support
Chrome Android – No support
Chrome Android
footnote No support
Firefox for Android – Preview support
Firefox for Android 151 (Release date: 2026-05-19)
footnote Preview browser support
Opera Android – No support
Opera Android
footnote No support
Safari on iOS – No support
Safari on iOS
footnote No support
Samsung Internet – No support
Samsung Internet
footnote No support
WebView Android – No support
WebView Android
footnote No support
WebView on iOS – No support
WebView on iOS
footnote No support

Legend

Tip: you can click/tap on a cell for more information.

Full support
Full support
In development. Supported in a pre-release version.
In development. Supported in a pre-release version.
No support
No support
Experimental. Expect behavior to change in the future.
Non-standard. Check cross-browser support before using.
User must explicitly enable this feature.
Uses a non-standard name
Has more compatibility info.

See also