Streaming DSD
When a server-rendered web component using Declarative Shadow DOM (DSD) is streamed to the browser, the HTML parser invokes the custom element constructor synchronously as soon as it encounters the host element’s start tag. This happens before the parser processes the element’s children, including the <template shadowrootmode="...">.
At the moment, there doesn’t appear to be a straightforward way to detect when the HTML parser has finished processing (or “upgrading”) that <template>.
Minimal reproduction example
The console shows the error:
A second declarative shadow root cannot be created on a host.
What’s happening
By the time the parser reaches the <template shadowrootmode>, the component’s JavaScript has already been registered and its constructor has run. If the constructor calls attachShadow(), the parser’s later attempt to attach the declarative shadow root fails.
This is due to the order of execution:
- The constructor runs immediately when the host element’s start tag is parsed.
- At that point:
- The element is not yet connected to the DOM.
- Its children have not been parsed.
- Its attributes may not yet be fully available.
As a result, the element appears effectively “empty,” making it unreliable to inspect or act on at this stage.
Current solution
A practical workaround is to defer the attachShadow() call to connectedCallback. At this point:
- The element is attached to the DOM.
- Its attributes can be read reliably.
Although the HTML parser may still be processing child nodes, this is usually sufficient to determine whether the component is:
- Hydrating server-rendered output, or
- Falling back to client-side rendering
If we are in the server rendered case we can decide:
- If a
shadowRoot already exists, the parser has finished processing the <template>, and execution can continue normally.
- If no
shadowRoot exists yet, the component must wait until parsing completes.
One way to handle this is by using a MutationObserver to detect when the shadow root becomes available. In a real-world scenario, you might also wait for a higher-level signal (e.g. a streaming completion marker) before proceeding with hydration.
Current solution example
Question:
Am I missing something, or is there currently no reliable way to detect when the HTML parser has finished processing the <template shadowrootmode> in a streaming scenario?
Streaming DSD
When a server-rendered web component using Declarative Shadow DOM (DSD) is streamed to the browser, the HTML parser invokes the custom element constructor synchronously as soon as it encounters the host element’s start tag. This happens before the parser processes the element’s children, including the
<template shadowrootmode="...">.At the moment, there doesn’t appear to be a straightforward way to detect when the HTML parser has finished processing (or “upgrading”) that
<template>.Minimal reproduction example
The console shows the error:
What’s happening
By the time the parser reaches the
<template shadowrootmode>, the component’s JavaScript has already been registered and its constructor has run. If the constructor callsattachShadow(), the parser’s later attempt to attach the declarative shadow root fails.This is due to the order of execution:
As a result, the element appears effectively “empty,” making it unreliable to inspect or act on at this stage.
Current solution
A practical workaround is to defer the
attachShadow()call toconnectedCallback. At this point:Although the HTML parser may still be processing child nodes, this is usually sufficient to determine whether the component is:
If we are in the server rendered case we can decide:
shadowRootalready exists, the parser has finished processing the<template>, and execution can continue normally.shadowRootexists yet, the component must wait until parsing completes.One way to handle this is by using a
MutationObserverto detect when the shadow root becomes available. In a real-world scenario, you might also wait for a higher-level signal (e.g. a streaming completion marker) before proceeding with hydration.Current solution example
Question:
Am I missing something, or is there currently no reliable way to detect when the HTML parser has finished processing the
<template shadowrootmode>in a streaming scenario?