<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, generated immediately and inserted into a shadow DOM, or used as part of out-of-order patching with <template for="...">.
Attributes
This element includes the global attributes.
for-
The
forattribute is used for out-of-order patching with<template for="...">, matching an equivalent<?start id="...">or<?marker "...">marker. See the out-of-order patching section and examples section. 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
ShadowRootobject 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 aShadowRoothas already been declaratively created in the same parent — then anHTMLTemplateElementis constructed. AHTMLTemplateElementcannot subsequently be changed into a shadow root after parsing, for example, by settingHTMLTemplateElement.shadowRootMode.Note: You may find the non-standard
shadowrootattribute in older tutorials and examples that used to be supported in Chrome 90-110. This attribute has since been removed and replaced by the standardshadowrootmodeattribute. shadowrootclonable-
Sets the value of the
clonableproperty of aShadowRootcreated using this element totrue. If set, a clone of the shadow host (the parent element of this<template>) created withNode.cloneNode()orDocument.importNode()will include a shadow root in the copy. shadowrootcustomelementregistry-
Sets the
customElementRegistryproperty of aShadowRootcreated using this element tonull, rather than the document's custom element registry. This allows a scopedCustomElementRegistryto be attached later usingCustomElementRegistry.initialize(). shadowrootdelegatesfocus-
Sets the value of the
delegatesFocusproperty of aShadowRootcreated using this element totrue. 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 tofalse. shadowrootreferencetarget-
Sets the value of the
referenceTargetproperty of aShadowRootcreated 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
serializableproperty of aShadowRootcreated using this element totrue. If set, the shadow root may be serialized by calling theElement.getHTML()orShadowRoot.getHTML()methods with theoptions.serializableShadowRootsparameter settrue. The value defaults tofalse. shadowrootslotassignment-
Sets the
slotAssignmentproperty of aShadowRootcreated using this element. This is the declarative equivalent of theslotAssignmentoption of theElement.attachShadow()method.named-
Elements are automatically assigned to
<slot>elements within this shadow root. This is the default value.Elements with the
slotattribute are assigned to the first<slot>in the template that has the correspondingnameattribute. If multiple elements specify the same slot name, they are all added to the first slot in the template that has that name, and rendered in the order they are declared. All unnamed elements — elements that don't specify aslotattribute — are assigned to the default slot in the order they are declared. This is the first unnamed<slot>in the template. manual-
Elements are manually assigned to particular slot elements using
HTMLSlotElement.assign(). No automatic assignment takes place.
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 three 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.
Other attributes prefixed with shadowroot allow declarative customization of the ShadowRoot, such as controlling how slots are assigned.
Out-of-order patching
Traditionally, HTML is delivered in order and read, processed, and displayed from top to bottom. To change that order, you can either hide or rearrange elements with CSS or update the DOM produced by the HTML afterwards with JavaScript. However, many pages are composed of multiple parts that may be ready to render at different times, or that may be more important to deliver to the user earlier.
The <template> element allows for delivering HTML out-of-order, which involves replacing processing instruction markers with the contents of the <template> element (also referred to as patching).
For example, a <?marker name="my-identifier"> processing instruction marker can be patched with the contents of a <template for="my-identifier"> element supplied much later in the HTML. See the Using <template for> for patching example.
As well as the <?marker> processing instructions marker, a <?start> and <?end> pair can be used to contain temporary content (for example, <?start name="my-identifier">Loading...<?end>), which is temporarily shown until the <template for="my-identifier"> is processed and the whole section is replaced. See the Using <template for> for range patching example.
When written in HTML, processing instructions can be provided with or without the trailing ?, and the browser will add it if not supplied when parsing the DOM. Both <?start?> and <?start> are therefore valid and parsed as <?start?>. XML is stricter and requires the trailing ?.
If the for attribute does not match any marker processing instruction name, the <template> content will remain hidden in the DOM and won't be used in any patch.
To prevent components from updating unrelated parts of the DOM, <template for="..."> elements can only patch markers inside the <template> parent's DOM tree. The only exception is <template> elements that are direct children of the <body> element — they can also patch <head> elements to allow updating <title> and other <head> elements.
Examples
>Generating table rows
First we start with the HTML portion of the example.
<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.
// 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.
<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>
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.
<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.
<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.
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).

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.

Declarative shadow DOM with named slot assignment
This example shows how elements can be assigned to slots in a shadow DOM based on their slot attribute (matched against the slot's name attribute).
HTML
First we define an <article> element that presents title, metadata, and article body information.
The article contains a <template> element that will become a shadow root, because of the presence of the shadowrootmode attribute.
We don't need to set its shadowrootslotassignment attribute because named slot assignment is the default.
The template defines elements that have named slots for "header" and "meta" information, and an unnamed slot for "body" information. The elements are styled differently so it is easy to differentiate them.
<article id="host">
<template shadowrootmode="open" shadowrootslotassignment="named">
<style>
.header {
background-color: plum;
}
.meta {
background-color: green;
}
.body {
background-color: lightblue;
}
</style>
<h2 class="header">
<slot name="title"></slot>
</h2>
<div class="meta">
<slot name="meta"></slot>
</div>
<div class="body">
<slot></slot>
</div>
</template>
<p>
Text 1 with no slot attribute. Goes into default (unnamed) slot inside the
"body" div.
</p>
<span slot="title">Text for the title slot</span>
<span slot="meta">Text for the meta slot</span>
<p>
Text 2 with no slot attribute. Also goes into default (unnamed) slot inside
the "body" div.
</p>
</article>
Inside the same host, below the template, we have four elements for populating the slots.
The <span> elements have slot attributes that match the name attributes on slots in the template, and will populate the corresponding slots.
The two <p> elements are unnamed, so are both inserted into the unnamed <slot> in the "body" element.
Results
The example below should show the content of the slots displayed in the appropriate sections.
Declarative shadow DOM with manual slot assignment
This example shows how elements can be assigned to slots in a shadow DOM using manual slot assignment.
With this approach, each element must manually be assigned to a particular slot. There is no default assignment, so any slot that is not assigned will be empty.
HTML
First we have a hidden support warning.
This warning is later set to be displayed via JavaScript if the browser doesn't support the shadowrootslotassignment attribute.
<p id="support-warning" hidden>
⛔ Your browser doesn't support the
<code>shadowrootslotassignment</code> attribute yet.
</p>
Next, we define an <article> element that presents title, metadata, and article body information.
This contains a <template> element that will become a shadow root, because of the presence of the shadowrootmode attribute, and will use manual slot assignment because shadowrootslotassignment="manual" is set.
The template defines elements that have slots for "header", "meta", and "body" information, that can be separately referenced by their id attribute.
The elements are styled differently so it is easy to differentiate them.
<article id="host">
<template shadowrootmode="open" shadowrootslotassignment="manual">
<style>
.header {
background-color: plum;
}
.meta {
background-color: green;
}
.body {
background-color: lightblue;
}
</style>
<h2 class="header">
<slot id="titleSlot"></slot>
</h2>
<div class="meta">
<slot id="metaSlot"></slot>
</div>
<div class="body">
<slot id="bodySlot"></slot>
</div>
</template>
<span id="text_title">Text for the title slot</span>
<span id="text_meta">Text for the meta slot</span>
<p id="text_body_1">Text 1 for body slot.</p>
<p id="text_body_2">Text 2 for body slot.</p>
</article>
Inside the same host, below the template, we have four elements for populating the slots. These are also identified by id.
JavaScript
The JavaScript for manual slot assignment is shown below.
First the code gets the slots within the shadow root, then the text to be inserted, and finally assigns the text to the slot.
Note that you can only assign a node once to any particular slot, and that if you assign multiple nodes to a single slot using HTMLSlotElement.assign(), the order they are specified controls the order they are added.
const host = document.querySelector("#host");
const shadow = host.shadowRoot;
// 1. Target your slots
const titleSlot = shadow.querySelector("#titleSlot");
const metaSlot = shadow.querySelector("#metaSlot");
const bodySlot = shadow.querySelector("#bodySlot");
// 2. Target the Elements to slot
const body1Text = document.querySelector("#text_body_1");
const body2Text = document.querySelector("#text_body_2");
const titleText = document.querySelector("#text_title");
const metaText = document.querySelector("#text_meta");
// 3. Manually assign them
titleSlot.assign(titleText);
metaSlot.assign(metaText);
bodySlot.assign(body2Text, body1Text);
The code displays the hidden support warning if slot assignment is not supported.
const isShadowRootSlotAssignmentSupported = Object.hasOwn(
HTMLTemplateElement.prototype,
"shadowRootSlotAssignment",
);
document
.querySelector("p[hidden]")
.toggleAttribute("hidden", isShadowRootSlotAssignmentSupported);
Results
The example below should show the content of the slots displayed in the appropriate sections.
Note:
If the shadowrootslotassignment attribute is not supported, a warning note is displayed and the browser will use named assignment.
However, because none of the slots or elements to be inserted are named, all the elements will be inserted into the title slot (because this is the first unnamed slot, and hence is the "default" slot).
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
<div id="container"></div>
<template id="template">
<div>Click me</div>
</template>
JavaScript
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.
Using <template for> for patching
This example uses the <?marker name="placeholder"> processing instruction as a placeholder and later on fills in the contents using <template for="placeholder">.
<body>
<div>
<?marker name="placeholder">
</div>
...
<template for="placeholder">Lorem Ipsum...</template>
...
</body>
Initially, this results in an empty <div> being rendered. It is then updated to the following after the <template> element is parsed and processed:
<div>
Lorem Ipsum...
</div>
Using <template for> for range patching
This example uses the <?start> and <?end> processing instructions to contain placeholder content, which is initially displayed then replaced by the <template for> content later on.
<body>
<div>
<?start name="placeholder">
Loading...
<?end>
</div>
...
<template for="placeholder">Lorem Ipsum...</template>
...
</body>
Initially the <div> is rendered with the Loading... placeholder content. This is then updated to the following after the <template> is parsed and processed:
<div>
Lorem Ipsum...
</div>
This example also demonstrates the lack of processing instruction children and nesting. The <?start> and <?end> processing instructions, although linked in terms of their relationship with <template for>, are separate nodes and not opening and closing tags. They therefore do not contain the Loading... content as a child (as demonstrated by the lack of indentation).
Using <template for> for patching <head> elements
This example shows that <template for> elements that are direct children of the <body> element can patch <head> markers.
<head>
...
<?start name="title"><title>Loading...</title><?end>
<?start name="meta-description"><meta name="description" contents="Loading..."><?end>
...
</head>
<body>
...
<template for="title"><title>The actual title of the page</title></template>
<template for="meta-description"><meta name="description" contents="This is a meaningful description..."></template>
...
</body>
Results in the following once the <template> elements have been parsed:
<head>
...
<title>The actual title of the page</title>
<meta name="description" contents="This is a meaningful description...">
...
</head>
<body>
...
</body>
Including markers in <template for> to allow contents to be repatched later
You can also insert markers inside <template for> elements, creating new placeholders to enable patching the same content multiple times. You can reuse existing name attributes.
For example, if you are building a Single Page Application (SPA) with <template for>, you may wish to allow the <title> to be patched on each route update, which could be achieved like this:
<head>
...
<?start name="title">
<title>Loading...</title>
<?end>
...
</head>
<body>
...
<template for="title"><?start name="title"><title>The actual title of the page</title><?end></template>
...
</body>
This will result in the following once the <template> element is parsed:
<head>
...
<?start name="title"><title>The actual title of the page</title><?end>
...
</head>
<body>
...
</body>
Later on, a new <template for="title"> could be inserted into the DOM to replace the <title> again.
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
See also
partandexportpartsHTML attributes<slot>HTML element:has-slotted,:host,:host(), and:host-context()CSS pseudo-classes::partand::slottedCSS pseudo-elementsShadowRootinterface- Using templates and slots
- CSS scoping module
- Declarative Shadow DOM (with html) in Using Shadow DOM
- Declarative shadow DOM on web.dev (2023)