The HTML shadowrootserializable attribute
The HTML shadowrootserializable attribute marks a declarative shadow root, created with <template shadowrootmode>, as serializable. Calling getHTML({ serializableShadowRoots: true }) on the host then includes that shadow tree in the returned HTML, which is off by default.
Overview
The shadowrootserializable attribute lets a declarative shadow root be written back out as HTML. It is a boolean attribute on a <template> that also has shadowrootmode, and it sets the new shadow root's serializable flag to true.
Serialization is opt-in twice. The shadow root has to be serializable, and the caller has to ask with element.getHTML({ serializableShadowRoots: true }). When both hold, the output contains a <template shadowrootmode="open" shadowrootserializable=""> wrapper around the shadow content, markup that the HTML parser reads as a declarative shadow root.
innerHTML and outerHTML never include shadow roots, whatever the flag says, and getHTML() with no options leaves them out too. Its shadowRoots option can include specific shadow roots even when they are not marked serializable.
The flag is set once, when the parser attaches the shadow root, and ShadowRoot.serializable is read-only afterward. MDN's compatibility data lists the attribute in Chrome and Edge 125 and Safari 18. Firefox 128 supports the matching serializable option of attachShadow(), but the table lists no Firefox support for the declarative attribute.
Syntax
<my-card>
<template shadowrootmode="open" shadowrootserializable>
<p>Shadow content</p>
</template>
</my-card>
Values
| Value |
|---|
| A boolean attribute: present or absent. |
Example
<div id="a"><template shadowrootmode="open" shadowrootserializable><b>Serializable</b></template></div>
<div id="b"><template shadowrootmode="open"><b>Not serializable</b></template></div>
<ul>
<li>getHTML() of #a: <code id="a-out"></code></li>
<li>getHTML() of #b: <code id="b-out"></code></li>
<li>innerHTML of #a: <code id="a-inner"></code></li>
</ul>
<script>
const opts = { serializableShadowRoots: true };
const a = document.getElementById('a');
const b = document.getElementById('b');
document.getElementById('a-out').textContent = a.getHTML(opts) || '(empty string)';
document.getElementById('b-out').textContent = b.getHTML(opts) || '(empty string)';
document.getElementById('a-inner').textContent = a.innerHTML || '(empty string)';
</script>
Best practices
- Add
shadowrootserializableonly to shadow roots you really need to read back as HTML, such as components you save or snapshot. - Leave closed shadow roots non-serializable unless you mean to expose them, because
getHTML()serializes a serializable root even when its mode is closed. - Pass
{ serializableShadowRoots: true }explicitly, sincegetHTML()without options returns only the light DOM. - Use the
shadowRootsoption ofgetHTML()when you need one specific shadow root, instead of marking every instance serializable. - Keep shadowrootmode on the same template, because without a valid mode no shadow root is created and the attribute does nothing.
Frequently asked questions
What does shadowrootserializable do?
Why does innerHTML not include my shadow DOM?
Does shadowrootserializable work with closed shadow roots?
Is shadowrootserializable supported in all browsers?
How do I make a shadow root serializable without the template attribute?
serializable: true to attachShadow(), as in host.attachShadow({ mode: 'open', serializable: true }). That is the JavaScript equivalent of shadowrootserializable.






















