Declarative HTML migration
Use this add-on path after the core migration if your v2 application used
@microsoft/fast-html, <f-template>, RenderableFASTElement,
TemplateElement, declarative attributeMap or observerMap options, or
declarative event handlers. Complete the
core migration first. If the declarative templates
were also server-rendered or hydrated, complete the
Hydration and SSR migration as well.
What changed
The @microsoft/fast-html package has been removed. Declarative HTML now ships
from focused @microsoft/fast-element path exports and integrates with normal
FASTElement.define() calls.
Key migration points:
- Import declarative APIs from
@microsoft/fast-elementpath exports. - Replace
RenderableFASTElement(...).defineAsync()with subclassdefine(). - Replace public
TemplateElementsetup withtemplate: declarativeTemplate(). - Replace
TemplateElement.options()with define extensions. - Replace
prepare()with standard element lifecycle code. - Update declarative event handlers to use
$einstead of baree.
Update imports
Import declarative HTML APIs from FAST Element path exports instead of
@microsoft/fast-html.
// Before
import { TemplateElement } from "@microsoft/fast-html";
import { deepMerge } from "@microsoft/fast-html/utilities.js";
// After
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
import { deepMerge } from "@microsoft/fast-element/declarative-utilities.js";
Map helpers are define extensions with their own path exports.
import { attributeMap } from "@microsoft/fast-element/attribute-map.js";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
Keep importing core FAST Element APIs from the root package export.
| API | Import path |
|---|---|
FASTElement, FAST, ElementController, definition/controller types |
@microsoft/fast-element |
attr, AttributeDefinition, converters, ValueConverter |
@microsoft/fast-element |
Observable, observable, volatile, Updates |
@microsoft/fast-element |
html, css, ViewTemplate, HTMLView |
@microsoft/fast-element |
Schema, schemaRegistry, schema types |
@microsoft/fast-element |
Binding, oneWay, oneTime, listener |
@microsoft/fast-element |
DOM, DOMAspect, DOMPolicy |
@microsoft/fast-element |
children, elements, ref, repeat, slotted, when |
@microsoft/fast-element |
Replace RenderableFASTElement
The RenderableFASTElement mixin has been removed. Components extend
FASTElement and call define() directly.
// Before
import { RenderableFASTElement } from "@microsoft/fast-html";
RenderableFASTElement(MyComponent).defineAsync({
name: "my-component",
templateOptions: "defer-and-hydrate",
});
// After
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
MyComponent.define({
name: "my-component",
template: declarativeTemplate(),
});
declarativeTemplate() coordinates template resolution. If code explicitly
observes the Promise returned by define(), it resolves after the matching
<f-template> supplies the concrete template. If prerendered content exists in
the DOM, call enableHydration() before elements connect to hydrate it.
Replace public TemplateElement setup
The <f-template> implementation is now internal and is defined automatically
when an element uses template: declarativeTemplate().
| Removed | Replacement |
|---|---|
TemplateElement public export |
declarativeTemplate() |
TemplateElement.define({ name: "f-template" }) |
No manual definition needed. |
TemplateElement.config(callbacks) |
enableHydration().whenHydrated(tagName) for tag-specific hydration waits and enableHydration().whenHydrated() for the active hydration batch. |
TemplateElement.options(...) |
attributeMap() and observerMap() define extensions. |
AttributeMap / ObserverMap class exports from the old declarative public surface |
attributeMap() / observerMap() helpers and config types. |
// Before
import { TemplateElement } from "@microsoft/fast-html";
TemplateElement.define({ name: "f-template" });
TemplateElement.options({
"my-element": {
attributeMap: true,
observerMap: true,
},
});
MyElement.define({ name: "my-element" });
// After
import { attributeMap } from "@microsoft/fast-element/attribute-map.js";
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
MyElement.define(
{
name: "my-element",
template: declarativeTemplate(),
},
[attributeMap(), observerMap()],
);
FASTElementDefinition.schema is optional. Declarative templates assign it
automatically during template resolution. Non-declarative users can provide a
manual schema on the definition, or pass one directly to observerMap({ schema }).
import { Schema } from "@microsoft/fast-element";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
const schema = new Schema("my-element");
MyElement.define({ name: "my-element" }, [observerMap({ schema })]);
Remove TemplateOptions
TemplateOptions, PartialFASTElementDefinition.templateOptions, and
FASTElementDefinition.templateOptions have been removed. Remove
templateOptions from element definitions and use
template: declarativeTemplate() when declarative markup should supply the
template.
// Before
MyElement.define({
name: "my-element",
templateOptions: "defer-and-hydrate",
});
// After
MyElement.define({
name: "my-element",
template: declarativeTemplate(),
});
Elements can still be defined before a template is attached. A later
FASTElementDefinition.template update notifies connected elements so they can
render or hydrate with the new template.
Update declarative attribute mapping
Declarative attribute mapping now defaults to "camelCase" instead of "none".
With the default strategy, {{firstName}} maps to the firstName property and
the first-name HTML attribute. If your v2 templates relied on literal
attribute names, configure both the client and server to use "none".
import { attributeMap } from "@microsoft/fast-element/attribute-map.js";
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
MyElement.define(
{
name: "my-element",
template: declarativeTemplate(),
},
[attributeMap({ "attribute-name-strategy": "none" })],
);
If server-rendered HTML is generated with @microsoft/fast-build, pass the same
strategy to the CLI:
fast build --attribute-name-strategy=none
Update declarative event handlers
Declarative event handlers now reserve only $e for the DOM event and $c for
the execution context. Bare e is no longer treated as the event object.
<!-- Before -->
<button @click="{handleClick(e)}"></button>
<!-- After -->
<button @click="{handleClick($e)}"></button>
If you used TemplateParser.hasDeprecatedEventSyntax in custom tooling, remove
that check as part of the migration.
Replace prepare() lifecycle logic
The prepare() hook from @microsoft/fast-html is no longer available. Move
initialization logic to connectedCallback() or an application-owned async
method that is called from connectedCallback().
// Before
class MyComponent extends FASTElement {
async prepare() {
this.data = await fetchData();
}
}
// After
class MyComponent extends FASTElement {
connectedCallback() {
super.connectedCallback();
this.loadData();
}
async loadData() {
this.data = await fetchData();
}
}
If prepare() previously guarded duplicate client fetches after SSR, use
this.$fastController.isPrerendered and this.$fastController.isHydrated to
decide whether client initialization should run.
Verify <f-template> loading order
A declarative FASTElement component requires a JavaScript class definition with
template: declarativeTemplate() and an <f-template> whose name matches the
custom element tag name.
<f-template name="my-counter">
<template>
<p>Count: {{count}}</p>
<button @click="{increment()}">+1</button>
</template>
</f-template>
import { attr, FASTElement } from "@microsoft/fast-element";
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
class MyCounter extends FASTElement {
@attr count: number = 0;
}
MyCounter.define({
name: "my-counter",
template: declarativeTemplate(),
});
Template-first and definition-first loading both work, but the custom element
registration completes only after the matching template is available. A common
pattern is to include <f-template> elements directly in the HTML page before
the script module loads.
Migration checklist
- Replace
@microsoft/fast-htmlimports with@microsoft/fast-element/declarative.jsand@microsoft/fast-element/declarative-utilities.js. - Replace
RenderableFASTElement(...).defineAsync()with subclassdefine(). - Remove manual
TemplateElement.define()calls. - Replace
TemplateElement.options()with define extensions passed as the second argument todefine(). - Remove
templateOptions; usetemplate: declarativeTemplate()when declarative markup supplies the template. - Configure
attributeMap({ "attribute-name-strategy": "none" })only if you need v2 literal attribute-name behavior, and align the server renderer option. - Replace bare
ein event handlers with$e. - Move
prepare()logic to standard element lifecycle code. - If declarative output is server-rendered, complete the Hydration and SSR migration.