ShadowRoot: innerHTML property

Warning: This property parses its input as HTML, writing the result into the DOM. APIs like this are known as injection sinks, and are potentially a vector for cross-site scripting (XSS) attacks, if the input originally came from an attacker.

You can mitigate this risk by always assigning TrustedHTML objects instead of strings and enforcing trusted types. See Security considerations for more information.

The innerHTML property of the ShadowRoot interface gets or sets the HTML markup to the DOM tree inside the ShadowRoot.

Value

Getting the property returns a string containing the HTML serialization of the shadow root's descendants.

Setting the property accepts either a TrustedHTML object or a string. It parses this value as HTML and replaces all the element's descendants with the result. When set to the null value, that null value is converted to the empty string (""), so shadowRoot.innerHTML = null is equivalent to shadowRoot.innerHTML = "".

Exceptions

SyntaxError DOMException

Thrown if an attempt was made to set the value of innerHTML using a string which is not properly-formed HTML.

TypeError

Thrown if the property is set to a string when Trusted Types are enforced by a CSP and no default policy is defined.

Description

innerHTML gets a serialization of the nested child DOM elements within the shadow root, or sets HTML or XML that should be parsed to replace the DOM tree within the shadow root.

Note that some browsers serialize the < and > characters as &lt; and &gt; when they appear in attribute values (see Browser compatibility). This is to prevent a potential security vulnerability (mutation XSS) in which an attacker can craft input that bypasses a sanitization function, enabling a cross-site scripting (XSS) attack.

Security considerations

The innerHTML property is a possible vector for cross-site scripting (XSS) attacks, where potentially unsafe strings provided by a user are injected into the DOM without first being sanitized. While the property does prevent <script> elements from executing when they are injected, it is susceptible to many other ways that attackers can craft HTML to run malicious JavaScript. For example, the following example would execute the code in the error event handler, because the <img> src value is not a valid image URL:

js
const name = "<img src='x' onerror='alert(1)'>";
shadowRoot.innerHTML = name; // shows the alert

You can mitigate these issues by always assigning TrustedHTML objects instead of strings, and enforcing trusted types using the require-trusted-types-for CSP directive. This ensures that the input is passed through a transformation function, which has the chance to sanitize the input to remove potentially dangerous markup before it is injected.

Examples

Reading the HTML contents of an element

Reading innerHTML causes the user agent to serialize the shadow root's descendants.

Given the following HTML:

html
<div class="host">
  <template shadowrootmode="open">
    <p>My name is Joe</p>
  </template>
</div>

You can get and log the markup for the shadow root as shown:

js
const shadowHost = document.querySelector("#host");
const shadowRoot = shadowHost.shadowRoot;
const contents = shadowRoot.innerHTML;
console.log(contents); // "\n  <p>My name is Joe</p>\n"

Setting the innerHTML of a Shadow root

In this example we'll replace an element's DOM by assigning HTML to the element's innerHTML property. To mitigate the risk of XSS, we'll first create a TrustedHTML object from the string containing the HTML, and then assign that object to innerHTML.

Trusted types are not yet supported on all browsers, so first we define the trusted types tinyfill. This acts as a transparent replacement for the Trusted Types JavaScript API:

js
if (typeof trustedTypes === "undefined")
  trustedTypes = { createPolicy: (n, rules) => rules };

Next we create a TrustedTypePolicy that defines a createHTML() method for transforming an input string into TrustedHTML instances. Commonly, implementations of createHTML() use a library such as DOMPurify to sanitize the input as shown below:

js
const policy = trustedTypes.createPolicy("my-policy", {
  createHTML: (input) => DOMPurify.sanitize(input),
});

Then we use this policy object to create a TrustedHTML object from the potentially unsafe input string, and assign the result to the element:

js
// The potentially malicious string
const untrustedString = "<p>I might be XSS</p><img src='x' onerror='alert(1)'>";

// Create a TrustedHTML instance using the policy
const trustedHTML = policy.createHTML(untrustedString);

// Get the shadow root
const shadowHost = document.querySelector("#host");
const shadowRoot = shadowHost.shadowRoot;

// Inject the TrustedHTML (which contains a trusted string)
shadowRoot.innerHTML = trustedHTML;

Warning: While you can directly assign a string to innerHTML, this is a security risk if the string to be inserted might contain potentially malicious content.

Specifications

Specification
HTML
# dom-shadowroot-innerhtml

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
innerHTML
Chrome – Full support
Chrome 53 (Release date: 2016-08-31)
footnote Full support
Edge – Full support
Edge 79 (Release date: 2020-01-15)
footnote Full support
Firefox – Full support
Firefox 63 (Release date: 2018-10-23)
footnote Full support
Opera – Full support
Opera 40 (Release date: 2016-09-20)
footnote Full support
Safari – Full support
Safari 10 (Release date: 2016-09-20)
footnote Full support
Chrome Android – Full support
Chrome Android 53 (Release date: 2016-09-07)
footnote Full support
Firefox for Android – Full support
Firefox for Android 63 (Release date: 2018-10-23)
footnote Full support
Opera Android – Full support
Opera Android 41 (Release date: 2016-10-25)
footnote Full support
Safari on iOS – Full support
Safari on iOS 10 (Release date: 2016-09-13)
footnote Full support
Samsung Internet – Full support
Samsung Internet 6 (Release date: 2017-08-23)
footnote Full support
WebView Android – Full support
WebView Android 53 (Release date: 2016-09-07)
footnote Full support
WebView on iOS – Full support
WebView on iOS 10 (Release date: 2016-09-13)
footnote Full support
Requires TrustedHTML instance when trusted types are enforced
Chrome – Full support
Chrome 83 (Release date: 2020-05-19)
footnote Full support
Edge – Full support
Edge 83 (Release date: 2020-05-21)
footnote Full support
Firefox – Full support
Firefox 148 (Release date: 2026-02-24)
footnote Full support
Opera – Full support
Opera 69 (Release date: 2020-06-24)
footnote Full support
Safari – Full support
Safari 26 (Release date: 2025-09-15)
footnote Full support
Chrome Android – Full support
Chrome Android 83 (Release date: 2020-05-19)
footnote Full support
Firefox for Android – Full support
Firefox for Android 148 (Release date: 2026-02-24)
footnote Full support
Opera Android – Full support
Opera Android 59 (Release date: 2020-06-30)
footnote Full support
Safari on iOS – Full support
Safari on iOS 26 (Release date: 2025-09-15)
footnote Full support
Samsung Internet – Full support
Samsung Internet 13 (Release date: 2020-12-02)
footnote Full support
WebView Android – Full support
WebView Android 83 (Release date: 2020-05-19)
footnote Full support
WebView on iOS – Full support
WebView on iOS 26 (Release date: 2025-09-15)
footnote Full support
Serializes < and > in attributes as &lt; and &gt; (see this spec issue)
Non-standard
Chrome – Full support
Chrome 138 (Release date: 2025-06-24)
footnote Full support
Edge – Full support
Edge 138 (Release date: 2025-06-26)
footnote Full support
Firefox – Full support
Firefox 140 (Release date: 2025-06-24)
footnote Full support
Opera – Full support
Opera 122 (Release date: 2025-09-11)
footnote Full support
Safari – Full support
Safari 26 (Release date: 2025-09-15)
footnote Full support
Chrome Android – Full support
Chrome Android 138 (Release date: 2025-06-24)
footnote Full support
Firefox for Android – Full support
Firefox for Android 140 (Release date: 2025-06-24)
footnote Full support
Opera Android – Full support
Opera Android 91 (Release date: 2025-08-19)
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 138 (Release date: 2025-06-24)
footnote Full support
WebView on iOS – Full support
WebView on iOS 26 (Release date: 2025-09-15)
footnote Full support

Legend

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

Full support
Full support
No support
No support
Non-standard. Check cross-browser support before using.