🐛 Bug Report
DSD hydration fails when light DOM text of a component contains text that resembles template binding syntax (double curly braces wrapping a property name), even when that text is not intended as a binding.
When using Declarative Shadow DOM (DSD) with RenderableFASTElement and defineAsync, FAST's hydration fails if a component's light DOM contains double-curly-brace interpolation patterns as display text. This is a blocker for any SSR/DSD application that shows code examples containing template syntax.
💻 Repro or Code Sample
The reproduction consists of two components:
- code-sample - A BTR component with a slot that displays code examples
- my-app - A parent BTR component whose template contains a code-sample with light DOM text that includes binding syntax
code-sample.html (template):
<div class="code-block">
<button type="button" @click="{toggleCode()}">
{ {toggleLabel} }
</button>
<div class="code-container">
<pre><slot></slot></pre>
</div>
</div>
Note: In actual code, there is no space between the curly braces. Spaces added here to prevent GitHub from interpreting them.
code-sample.ts:
import { FASTElement, observable } from '@microsoft/fast-element';
import { RenderableFASTElement } from '@microsoft/fast-html';
class CodeSample extends RenderableFASTElement(FASTElement) {
@observable codeOpen = false;
@observable toggleLabel = 'Show Code';
toggleCode(): void {
this.codeOpen = !this.codeOpen;
this.toggleLabel = this.codeOpen ? 'Hide Code' : 'Show Code';
}
}
CodeSample.defineAsync({
name: 'code-sample',
templateOptions: 'defer-and-hydrate',
});
my-app.html (parent template - this triggers the bug):
<h2>Click Handler Example</h2>
<div class="preview">
<button @click="{onClick()}">Click Me</button>
<span>{ {clickMessage} }</span>
</div>
<code-sample>
<code>
button @click="{onClick()}" Click Me /button
span { {clickMessage} } /span
@observable clickMessage = '';
onClick() { this.clickMessage = 'Clicked!'; }
</code>
</code-sample>
my-app.ts:
import { FASTElement, observable } from '@microsoft/fast-element';
import { RenderableFASTElement } from '@microsoft/fast-html';
import './code-sample.js';
class MyApp extends RenderableFASTElement(FASTElement) {
@observable clickMessage = '';
onClick(_event: Event | null): void {
this.clickMessage = 'Clicked!';
}
}
MyApp.defineAsync({
name: 'my-app',
templateOptions: 'defer-and-hydrate',
});
After SSR rendering, FAST's template compiler creates binding markers for the double-curly-brace text inside the code element within code-sample, even though that text is meant to be literal display content slotted into a child component, not a binding expression.
🤔 Expected Behavior
- The double-curly-brace text inside the code element should be treated as literal text. It is light DOM content slotted into code-sample and is not a binding expression.
- Hydration should succeed without errors.
- The code example text should be visible as-is in the rendered page.
😯 Current Behavior
Hydration produces console errors like:
FAST: Binding 14 was not found in the DOM. Hydration failed.
The hydration process creates binding nodes for the double-curly-brace text inside the code element, but the pre-rendered DSD does not have corresponding binding markers at those positions, causing a mismatch.
Workarounds attempted:
| Approach |
Result |
| HTML entity escaping the curly braces |
Hydration errors persist - entities are decoded before FAST processes them |
| HTML comments wrapping the text |
No hydration errors, but text is invisible in the UI |
| script type=text/plain wrapper |
SSR renderer still processes content inside script tags |
| Modifying code-sample connectedCallback |
Adding connectedCallback causes a new hydration mismatch |
No viable workaround exists that both prevents hydration errors AND keeps the code text visible.
💁 Possible Solution
Consider one of:
-
Escape syntax: Provide an official escape sequence for double-curly-braces in templates so developers can include literal text without triggering the binding parser.
-
Opt-out attribute: Allow elements to mark their content as non-bindable, e.g. a data-fast-no-bind attribute. The template compiler would skip binding processing for children of such elements.
-
Semantic element awareness: The template compiler could skip binding processing for text content inside code and pre elements, since these are semantically preformatted/literal content.
We would be happy to contribute a fix or test if there is a preferred direction.
🔦 Context
We are building a component gallery (similar to Storybook) as an SSR/DSD application using @microsoft/fast-html. The gallery displays code examples for each component showing how to use template syntax (event bindings, interpolations). Every code sample that contains double-curly-brace patterns triggers hydration failures, making it impossible to show template syntax examples in a DSD-rendered app. This affects approximately 240 code sample blocks across 33 component story pages.
🌍 Your Environment
- OS & Device: Windows 11 on PC
- Browser: Microsoft Edge (Chromium)
- Version: @microsoft/fast-element 2.10.4, @microsoft/fast-html 1.0.0-alpha.50
🐛 Bug Report
DSD hydration fails when light DOM text of a component contains text that resembles template binding syntax (double curly braces wrapping a property name), even when that text is not intended as a binding.
When using Declarative Shadow DOM (DSD) with RenderableFASTElement and defineAsync, FAST's hydration fails if a component's light DOM contains double-curly-brace interpolation patterns as display text. This is a blocker for any SSR/DSD application that shows code examples containing template syntax.
💻 Repro or Code Sample
The reproduction consists of two components:
code-sample.html (template):
Note: In actual code, there is no space between the curly braces. Spaces added here to prevent GitHub from interpreting them.
code-sample.ts:
my-app.html (parent template - this triggers the bug):
my-app.ts:
After SSR rendering, FAST's template compiler creates binding markers for the double-curly-brace text inside the code element within code-sample, even though that text is meant to be literal display content slotted into a child component, not a binding expression.
🤔 Expected Behavior
😯 Current Behavior
Hydration produces console errors like:
The hydration process creates binding nodes for the double-curly-brace text inside the code element, but the pre-rendered DSD does not have corresponding binding markers at those positions, causing a mismatch.
Workarounds attempted:
No viable workaround exists that both prevents hydration errors AND keeps the code text visible.
💁 Possible Solution
Consider one of:
Escape syntax: Provide an official escape sequence for double-curly-braces in templates so developers can include literal text without triggering the binding parser.
Opt-out attribute: Allow elements to mark their content as non-bindable, e.g. a data-fast-no-bind attribute. The template compiler would skip binding processing for children of such elements.
Semantic element awareness: The template compiler could skip binding processing for text content inside code and pre elements, since these are semantically preformatted/literal content.
We would be happy to contribute a fix or test if there is a preferred direction.
🔦 Context
We are building a component gallery (similar to Storybook) as an SSR/DSD application using @microsoft/fast-html. The gallery displays code examples for each component showing how to use template syntax (event bindings, interpolations). Every code sample that contains double-curly-brace patterns triggers hydration failures, making it impossible to show template syntax examples in a DSD-rendered app. This affects approximately 240 code sample blocks across 33 component story pages.
🌍 Your Environment