Technical Documentation: microsoft/fast
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
FAST
:star: We appreciate your star, it helps!
Introduction
FAST is dedicated to providing support for native Web Components and modern Web Standards, and it's designed to help you efficiently tackle some of the most common challenges in website and application design and development.
TL;DR
- Create reusable UI components with
@microsoft/fast-element, all based on W3C Web Component standards. - Integrate FAST Web Components with any library, framework, or build system. You can adopt incrementally without re-writing your existing systems.
For an in-depth explanation of FAST see our docs introduction.
Packages
`@microsoft/fast-element`
The @microsoft/fast-element library is a lightweight means to easily build performant, memory-efficient, standards-compliant Web Components. FAST Elements work in every major browser and can be used in combination with any front-end framework or even without a framework. To get up and running with @microsoft/fast-element see the Getting Started guide.
`@fluentui/web-components`
@fluentui/web-components is a library of Web Components based on the Fluent design language, built with @microsoft/fast-element.
The source for @fluentui/web-components is hosted in the Fluent UI monorepo.
Getting Started
We hope you're excited by the possibilities that FAST presents. But, you may be wondering where to start. Here are a few statements that describe various members of our community. We recommend that you pick the statement you most identify with and follow the links where they lead. You can always come back and explore another topic at any time.
- "I just want ready-made components!"
- "I want to build my own components."
- "I need to integrate FAST with another framework or build system."
- "I want to look at a quick reference."
- Jump to the Cheat Sheet (only available in 1.x)
Roadmap
Major features for the FAST project can be found in the roadmap.
Joining the Community
Looking to get answers to questions or engage with us in realtime? Our community is most active on Discord. Submit requests and issues on GitHub, or join us by contributing on some good first issues via GitHub.
Get started here with the Contributor Guide.
We look forward to building an amazing open source community with you!
Contact
- Join the community and chat with us in real-time on Discord.
- Submit requests and issues on GitHub.
- Contribute by helping out on some of our recommended first issues on GitHub.
Overview (`examples/README.md`)
FAST example apps
The examples/ workspace hosts small, focused applications that show how to
build real UI with FAST using production-shaped tooling, app structure, and
styling. It is aimed at contributors, library consumers, and anyone evaluating
FAST patterns in a complete app context.
Apps
| Folder | Package | Purpose |
|---|---|---|
design-system |
@microsoft/fast-examples-design-system |
Shared CSS design tokens (no JS) consumed by every example app. |
csr/todo-app |
@microsoft/fast-todo-app-example |
A To-Do app demonstrating @microsoft/fast-element patterns end to end, styled with the shared design-system tokens (light theme, no runtime toggle). |
csr/todo-mobx-app |
@microsoft/fast-todo-mobx-app-example |
A To-Do app showing how to integrate MobX state with @microsoft/fast-element using a single autorun per component (no custom bridge code), styled with the shared design-system tokens. |
ssr/chat-app |
@microsoft/fast-chat-app-example |
A declarative FAST chat demo that pre-renders with @microsoft/fast-build and streams canned assistant replies into the transcript, styled with the shared design-system tokens (dark theme, no runtime toggle). |
ssr/webui-todo-app |
@microsoft/fast-webui-todo-app-example |
A To-Do app pre-rendered by @microsoft/webui and hydrated by @microsoft/fast-element/declarative.js, styled with the shared design-system tokens (light theme, no runtime toggle). |
Example apps are grouped by rendering strategy. Client-side-rendered (CSR)
apps live under examples/csr/ and server-side-rendered (SSR) apps
live under examples/ssr/.
Shared design system
Every example app must consume @microsoft/fast-examples-design-system. The
package is CSS-only — it ships a single stylesheet (tokens.css) and no
JavaScript or TypeScript. Apps wire theme switching themselves using thedata-theme attribute on <html>.
The package exposes a single import:
| Import | What it does |
|---|---|
@microsoft/fast-examples-design-system/tokens.css |
Declares every token on :root. Color and elevation values use light-dark(); theme is controlled by prefers-color-scheme (default) or <html data-theme="light"|"dark"> (forced). |
Style components with semantic tokens such as--fast-background-layer-primary-solid,--fast-foreground-ctrl-neutral-primary-rest,--fast-background-ctrl-brand-rest, --fast-text-global-body3-font-size,--fast-padding-content-medium, and --fast-corner-large. Never hard-code
colors, font sizes, padding, gaps, corners, stroke widths, shadows,
durations, or curves.
See:
examples/design-system/README.md— quickstart,
available imports, token catalog.examples/design-system/DESIGN.md— naming
grammar, role catalog, theme model, and rules for adding tokens (the
canonical authoring guide for both humans and coding agents).examples/DESIGN.md— workspace-level guidance for building
new example apps on top of the design system.
Creating a new example app
- Scaffold a new folder under the appropriate rendering-strategy subfolder
(CSR apps go underexamples/csr/<your-app>/; SSR apps go underexamples/ssr/<your-app>/). - Use
examples/csr/todo-app/as a reference for CSR
apps, orexamples/ssr/chat-app//examples/ssr/webui-todo-app/as a reference
for SSR apps (package.json,tsconfig.json, build config, entry HTML). - Add
"@microsoft/fast-examples-design-system": "^1.0.0"to the new
app'sdependenciesand runnpm installfrom the repo root. - Import
@microsoft/fast-examples-design-system/tokens.cssexactly once at
the app entry point (typicallysrc/main.tsorsrc/index.ts). For an
intentionally single-theme app, hard-code<html data-theme="light">(or"dark") in the entry HTML and never touch it from JavaScript. - Reference tokens via
var(--fast-...)in componentcsstemplate
literals or.cssfiles — do not hard-code design values. - If the app needs a theme toggle, set or remove the
data-themeattribute
on<html>from your app code:Do not import any JavaScript from the design-system package — ittsdocument.documentElement.setAttribute("data-theme", "dark"); document.documentElement.removeAttribute("data-theme"); // restore system
intentionally exposes none. - Add the new app to the table at the top of this file.
Running an example
From the repository root:
npm install
npm start -w @microsoft/fast-<your-app>-exampleFor the existing example apps:
npm start -w @microsoft/fast-todo-app-example
npm start -w @microsoft/fast-todo-mobx-app-example
npm start -w @microsoft/fast-chat-app-example
npm start -w @microsoft/fast-webui-todo-app-exampleUseful links
Overview (`packages/README.md`)
FAST Packages
Our web component packages.
`@microsoft/fast-element`
The @microsoft/fast-element library is a lightweight means to easily build performant, memory-efficient, standards-compliant Web Components. FAST Elements work in every major browser and can be used in combination with any front-end framework or even without a framework. To get up and running with @microsoft/fast-element see the Getting Started guide.
`@microsoft/fast-router`
The @microsoft/fast-router package contains a history-based navigation and routing solution designed around Web Components. By using fast-router, you can create multi-page and full application experiences. The router works with any Web Components, but has special support for Web Components built on FAST.
Overview (`.github/workflows/README.md`)
Testing
To test workflows, use the GitHub CLI and trigger the workflow from a branch.
For more information see the GitHub CLI documentation.
Draft Pull Requests
All CI workflows that run against pull requests are configured to skip draft PRs:
- GitHub Actions (
ci-validate-pr.yml,ci-validate-platforms.yml,ci-validate-rust.yml): Thepull_requesttrigger includesready_for_reviewin its event types, and each job has a condition that skips execution when the PR is a draft. When a draft PR is marked as ready for review, the workflows will automatically trigger. - Azure Pipelines (
azure-pipelines-bench.yml,azure-pipelines-ci.yml): Theprtrigger usesdrafts: falseto prevent pipeline runs on draft PRs.
Continuous Deployment
Nightly publishing is coordinated across GitHub Actions and Azure Pipelines so that npm credentials never leave the Azure environment. GitHub Releases are the source of truth, and deployed/<tag> git marker tags track which releases have already been published.
cd-github-releases.yml(GitHub Actions) runs nightly via cron (0 8 * * *UTC, ~12am PST) and onworkflow_dispatch. It does not bump versions or push source changes tomain— version bumps land onmainthrough ordinary human-authored pull requests (for example, by runningnpm run bumplocally and opening a PR). The cron is scheduled ~1 hour before the Azure CD pipeline (09:00 UTC) so any GitHub releases this job creates are picked up by that same night's publish run. The workflow has two jobs:detect— checks outmainwithfetch-depth: 0and runsbuild/scripts/create-github-releases.mjs --check-only. The script walks the workspaces tree (nonpm cirequired), computes${name}_v${version}for every non-private workspace, and emitshasMissingReleases=trueif any of those git tags do not yet exist.releaseruns only when missing releases exist. Installs Node, the Rust toolchain (forcargo package), and the npm workspace dependencies, builds the repo, then runs the script in default mode. For every missing release the script: packs the npm tarball intopublish_artifacts_npm/, packs any paired Rust crates intopublish_artifacts_crates/, and creates the GitHub release with all assets attached viagh release create --target <sha>.@microsoft/fast-buildis a bundled release: it uses one npm package, one tag, and one GitHub release containing bothmicrosoft-fast-buildandmicrosoft-fast-convertcrate assets. TheghCLI creates the git tag atomically with the release, so "tag exists" and "release exists" are always the same fact — a failed release is safely retried on the next workflow run, with no orphan tag stranded behind. The script errors if a paired crate's version does not match the npm package's version — but this is purely a safety net: thepostbumphook inbeachball.config.jsrewrites each crate'sCargo.toml(and the matching entry inCargo.lock) automatically whenevernpm run bumpbumps the paired npm package, so they stay in sync from the same commit.
azure-pipelines-cd.yml(Azure Pipelines) runs every night at 1am PST (0 9 * * *UTC) withalways: trueso it still runs on no-op nights (it is checking external GitHub state, not repo commits). It is split into two stages so the heavy publish work is skipped on no-op nights:Check— runsbuild/scripts/download-github-releases.mjs --check-only. The script walks the current publishable workspaces, keeps only workspaces whose current${name}_v${version}release tag exists, filters out tags that already have adeployed/<tag>counterpart, and emits Azure Pipelines output variables for the overall deployment decision, npm dist-tag, and each package-specific release tag. No network calls to GitHub, npm, or crates.io are needed.Package— depends onCheckand runs only whenneedsDeployment == 'true'. ItsPrepareReleaseInputsjob downloads undeployed release assets through thefastGitHub service connection, sorts them intopublish_artifacts_npm/(.tgz) andpublish_artifacts_crates/(.crate), configures the detected npm dist-tag, and publishes those files as a governed pipeline artifact. The checkout-freeDeployrelease job consumes that artifact throughtemplateContext.inputs, thenFAST.Release.PipelineTemplate.yml@fastPipelinesperforms the actualnpm publish/cargo publish. After publishing succeeds, the ordinaryMarkDeployedjob checks out the repository and pushes adeployed/<tag>marker for each release. The next nightly run sees those markers and skips the corresponding releases.
Both scripts are thin Node.js wrappers around existing CLI tools and repository metadata — no extra npm dependencies and no custom GitHub API client. Idempotency is enforced entirely through git tags (${name}_v${version} on the GitHub side, deployed/${name}_v${version} on the Azure side), so neither side needs to talk to npm.org or crates.io to decide whether work is required.
Adding a publishable package
cd-github-releases.yml discovers publishable workspaces automatically from the root package.json workspaces list, but azure-pipelines-cd.yml must be updated because Azure Pipelines cannot create DownloadGitHubRelease@0 tasks dynamically from the runtime detection output.
The npm run checkchange command runs build/scripts/check-publish-pipeline.mjs to verify that every non-private workspace has matching Azure CD variables and a conditional DownloadGitHubRelease@0 task. This guardrail runs in PR validation and fails when a new publishable package is added without updating the publish pipeline.
When adding a new non-private workspace that should publish through CD:
- Ensure the workspace is included in the root
package.jsonworkspaceslist and has anameandversion. - If the package has paired crate assets, place each crate at
crates/<crate-name>/Cargo.toml. By default,<crate-name>is the npm package name with the leading@removed and/replaced by-.@microsoft/fast-buildis the special bundled release and pairs with bothcrates/microsoft-fast-build/Cargo.tomlandcrates/microsoft-fast-convert/Cargo.toml. - Add package-specific output variables to the
Packagestage inazure-pipelines-cd.yml. The output prefix is generated from the npm package name by converting@microsoft/<name>to camel case. For example,@microsoft/fast-fooemitsfastFooNeedsDeploymentandfastFooReleaseTag. - Add a conditional
DownloadGitHubRelease@0task toPrepareReleaseInputsusing thefastGitHub service connection,defaultVersionType: 'specificTag', and the package's$(ReleaseTag)variable. Release jobs cannot check out source or directly acquire undeclared dependencies, so all downloaded assets must remain part of the governedfast-release-inputspipeline artifact. - Confirm the artifact sorting step still covers the package assets. Packages should attach
.tgzassets, and paired crates should also attach.crateassets.
Example Azure additions for @microsoft/fast-foo:
variables:
fastFooNeedsDeployment: $[ stageDependencies.Check.CheckVersion.outputs['deploymentCheck.fastFooNeedsDeployment'] ]
fastFooReleaseTag: $[ stageDependencies.Check.CheckVersion.outputs['deploymentCheck.fastFooReleaseTag'] ]
steps:
- task: DownloadGitHubRelease@0
displayName: "Download @microsoft/fast-foo release assets"
condition: and(succeeded(), eq(variables['fastFooNeedsDeployment'], 'true'))
inputs:
connection: fast
userRepository: microsoft/fast
defaultVersionType: 'specificTag'
version: '$(fastFooReleaseTag)'
downloadPath: '$(System.ArtifactsDirectory)'Overview (`crates/microsoft-fast-build/README.md`)
microsoft-fast-build
A server-side renderer for FAST declarative HTML templates, written in pure Rust with no native runtime dependencies.
It takes FAST declarative HTML template syntax and a JSON state object, resolves bindings, evaluates conditionals, iterates repeats, and renders custom elements — producing static HTML ready to be served.
Installation
Add the crate to your Cargo.toml:
[dependencies]
microsoft-fast-build = "0.1"Or use cargo add:
cargo add microsoft-fast-buildUsage
Render with a JSON string
use microsoft_fast_build::render_template;
let html = render_template(
"<h1>{{title}}</h1>",
r#"{"title": "Hello, World!"}"#,
)?;
// html == "<h1>Hello, World!</h1>"Render with a parsed `JsonValue`
use microsoft_fast_build::{render, JsonValue};
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("name".to_string(), JsonValue::String("Alice".to_string()));
let state = JsonValue::Object(map);
let html = render("<p>{{name}}</p>", &state)?;
// html == "<p>Alice</p>"Both functions return Result<String, RenderError>. See Error Handling.
Render simulated stream chunks
Streaming APIs return precomputed HTML chunks in memory. Joining the chunks
matches the equivalent non-streamed render, while chunk boundaries are chosen
around content bindings and custom elements. Stream rendering uses the same
top-level preprocessing as normal rendering, including automatic escaping of
FAST-like syntax inside <code> elements.
use microsoft_fast_build::{render_template, render_template_stream};
let chunks = render_template_stream(
"<p>{{message}}</p>",
r#"{"message": "Hello"}"#,
None,
)?;
let html = render_template(
"<p>{{message}}</p>",
r#"{"message": "Hello"}"#,
None,
)?;
assert_eq!(chunks, vec!["<p>", "Hello", "</p>"]);
assert_eq!(chunks.join(""), html);Custom element streaming uses the *_stream_with_locator andrender_entry_*_stream_with_locator APIs. For entry HTML, a custom element's
opening tag chunk includes the complete Declarative Shadow DOM template; light
DOM content follows in later chunks. Template host attributes declared on the
inner <template> element are merged onto the streamed host opening tag with
the same author-wins and client-only-attribute filtering rules as normal
rendering.
Plugin integration APIs
render_shadow_root_with_locator(element_name, state, locator, config) renders
only the complete Declarative Shadow DOM <template> fragment. Its JSON-state
counterpart is render_shadow_root_template_with_locator(element_name, state: Option<&str>, locator, config). Pass resolved component state;
these APIs do not synthesize a host, overlay host attributes, emit template-host
attributes, or render caller-owned light DOM. Nested components, code-sample
escaping, and fresh v3/v2 hydration scopes use the whole-host rendering path.
Unknown template names and invalid JSON state return explicit errors.
use microsoft_fast_build::{
parse_f_templates, render_shadow_root_template_with_locator, Locator,
};
let definitions = parse_f_templates(
r#"<f-template name="my-card"><template>{{title}}</template></f-template>"#,
);
let locator = Locator::from_metadata(definitions)?;
let fragment = render_shadow_root_template_with_locator(
"my-card", Some(r#"{"title":"Hello"}"#), &locator, None,
)?;parse_f_templates is also a public native API. It returns Vec<FTemplate>
with format_version, bindings, name/content, and host/shadow-root attributes.BindingMetadata records direct names, expressions, syntax kinds, lexical
paths, and nested BindingScope { variable, iterable } frames. Application
types and required/optional declarations remain unknown (None); repeat
collections alone have a known array requirement. Metadata describes syntax,
not a state schema or a full validation certificate. Code samples are excluded.Locator::from_metadata validates names, duplicates, and the format version.
Legacy content/tuple constructors remain supported.
Each BindingMetadata also has a span: SourceSpan containing start and endSourcePosition records. Positions refer to the complete original input toparse_f_templates, including document prefixes and whitespace removed from the
returned content. offset is zero-based UTF-16, not a Rust byte index; line
and column are one-based, with columns measured in UTF-16 code units. Tabs count
as one column and non-BMP characters as two. CRLF is one line break and two offset
units; lone CR and LF each start a new line.
Spans are half-open and cover trimmed expressions without braces. Repeat spans
cover the iterable, matching the existing expression metadata; lexical scopes are
unchanged. Attribute entities retain their authored spelling. Code-sample
preprocessing does not shift later spans. Bindings and FAST directive markup
inside HTML comments remain unsupported.
The WASM exports are parse_f_templates(html) (JSON metadata array) andrender_shadow_root(elementName, templatesJson, state?, attributeNameStrategy?, markers_v2?). Generated declarations include FTemplateMetadata,TemplateBinding, TemplateBindingScope, TemplateSourceSpan, andTemplateSourcePosition. Each generated record hasformatVersion: 1; render exports reject unsupported or malformed explicit
versions, while still accepting unversioned maps and string-valued definitions.
Nullable and boolean attributes
Missing or explicit null bindings omit the entire attribute, including when
embedded in a literal value such as href="/prefix/{{value}}". An explicit
empty string remains href="". Boolean JSON values bound to known HTML boolean
attributes use presence/absence; ordinary attributes such as aria-hidden
still serialize booleans as "true"/"false". ?name="{{expr}}" remains the
recommended truthy/falsy form for matching browser-side FAST behavior.
Omitted bindings still reserve hydration slots. An author binding that resolves
to absence overrides template-host defaults rather than reintroducing them.
See the migration guide
for intentional changes from earlier output.
Template Syntax
Bindings and FAST directive markup inside HTML comments are unsupported.
Commenting out bound markup does not reliably disable its bindings and can cause
rendering errors or client hydration mismatches. Remove unused bound markup
instead, and keep explanatory comments free of binding syntax and FAST directive
tags.
Content Bindings
| Syntax | Description |
|---|---|
{{expr}} |
HTML-escaped value from state |
{{{expr}}} |
Raw (unescaped) HTML value from state |
<h1>{{title}}</h1>
<div>{{{richContent}}}</div>Single-Brace Passthrough
Single-brace expressions ({expr}) are FAST client-side-only bindings (event handlers, attribute directives). They are never interpreted by the server renderer.
In non-hydration rendering they pass through verbatim. When rendering Declarative Shadow DOM (inside a custom element's shadow template), client-side attribute directives — f-ref, f-slotted, f-children — are stripped from the HTML output, just like @event and :property bindings. The data-fe binding count still includes them so the FAST runtime can allocate the correct number of binding slots.
<slot f-slotted="{slottedNodes}"></slot>
<video f-ref="{video}"></video>
<ul f-children="{listItems}"></ul>
<slot data-fe="1"></slot>
<video data-fe="1"></video>
<ul data-fe="1"></ul>Boolean Attribute Bindings — `?attr`
The ?attr="{{expr}}" syntax is a FAST convention for conditionally rendering a boolean HTML attribute. The renderer evaluates expr against the element's state:
- truthy → the bare attribute name is emitted (e.g.
disabled) - falsy → the attribute is omitted entirely
<input type="checkbox" ?disabled="{{!isEnabled}}">
<input ?disabled="{{activeGroup == currentGroup}}" type="button">
<input type="checkbox" disabled data-fe="1">
<input type="button" data-fe="1">The data-fe marker is still emitted so the FAST client runtime knows to reconnect the binding during hydration.
Dataset Attribute Bindings — `dataset.propertyName`
FAST elements follow the MDN HTMLElement.dataset convention: camelCase property names (e.g. dateOfBirth) correspond to kebab-case data-* HTML attributes (e.g. data-date-of-birth).
Passing `data-*` attributes to custom elements
When a custom element receives data-* attributes, the renderer groups them under a nested "dataset" key in the child state so that {{dataset.X}} bindings in the shadow template resolve naturally:
<test-el data-date-of-birth="{{dob}}"></test-el>
<div data-date-of-birth="{{dataset.dateOfBirth}}"></div>With parent state {"dob": "1990-01-01"}, the shadow template receives child state {"dataset": {"dateOfBirth": "1990-01-01"}} and renders:
<div data-date-of-birth="1990-01-01" data-fe="1"></div>The data-* → dataset.* mapping uses the same camelCase conversion as the browser: data-date-of-birth → dataset.dateOfBirth.
Using `{{dataset.X}}` bindings
{{dataset.X}} is ordinary dot-notation: it reads state["dataset"]["X"]. The dataset key must be present in state, either from a data-* attribute on the enclosing custom element or from a state object you supply directly:
<span>{{dataset.name}}</span>
<f-when value="{{dataset.active}}">Active</f-when>ARIA Attribute Bindings — `ariaPropertyName`
ARIA attributes follow the Element ARIA reflection convention: camelCase property names (e.g. ariaDisabled) correspond to aria-* HTML attributes (e.g. aria-disabled).
Passing `aria-*` attributes to custom elements
When a custom element receives aria-* attributes, the renderer converts them to their camelCase property name and stores them as top-level state keys using a static lookup table:
<test-el aria-label="Close dialog" aria-disabled="true"></test-el>
<button aria-label="{{ariaLabel}}" aria-disabled="{{ariaDisabled}}">×</button>The shadow template receives child state {"ariaLabel": "Close dialog", "ariaDisabled": "true"}.
A lookup table is used instead of algorithmic conversion because ARIA attribute names do not place dashes at word boundaries — for example, aria-valuenow (not aria-value-now) maps to ariaValueNow.
HTML Attribute-to-Property Mappings
Some HTML attributes have DOM property names that differ from their attribute names (e.g. tabindex → tabIndex, readonly → readOnly). When a custom element receives these attributes, the renderer converts them to their camelCase property name:
<test-el tabindex="0" readonly="true"></test-el>
<input tabindex="{{tabIndex}}" readonly="{{readOnly}}">The shadow template receives child state {"tabIndex": "0", "readOnly": "true"}.
Only attributes where the property name differs are in the lookup table. Attributes like disabled, title, and hidden whose attribute and property names are identical pass through as-is.
Conditional Rendering — ``
<f-when value="{{isAdmin}}">Admin panel</f-when>
<f-when value="{{!isGuest}}">Member content</f-when>
<f-when value="{{status == 'active'}}">Active</f-when>
<f-when value="{{count > 0}}">Has items</f-when>
<f-when value="{{a || b}}">Either</f-when>
<f-when value="{{a && b}}">Both</f-when>Supported operators: ==, !=, >=, >, <=, <, ||, &&, !
Operators can be chained — && binds tighter than ||, matching standard precedence:
<f-when value="{{isAdmin && !isGuest && status == 'active'}}">Admin</f-when>
<f-when value="{{a || b || c}}">Any of the above</f-when>
<f-when value="{{isPublished || isAdmin && canPreview}}">Visible</f-when>Right-hand operands can be string literals ('foo'), boolean literals (true/false), number literals (42), or other state references.
When value is a bare reference (e.g. {{items}}), the value is coerced to a boolean using JavaScript semantics. Because the input state is JSON, users cannot provide undefined directly; however, missing state properties or unresolved paths behave like JavaScript undefined and are treated as falsy.
| Type | Falsy | Truthy |
|---|---|---|
null |
always | — |
boolean |
false |
true |
number |
0, NaN |
any other |
string |
"" |
any non-empty |
array |
— | always (even []) |
object |
— | always (even {}) |
Array Iteration — ``
<ul>
<f-repeat value="{{item in items}}">
<li>{{item.name}} — {{title}}</li>
</f-repeat>
</ul>Inside <f-repeat>, {{item}} resolves to the current loop variable and {{$index}} resolves to the 0-based iteration index. Other bindings fall back to the root state (e.g. {{title}} above).
If the list binding or dot path is missing, <f-repeat> treats it as an empty array and renders zero iterations. Present non-array values still return RenderError::NotAnArray.
<f-repeat value="{{row in rows}}">
<tr data-index="{{$index}}">
<td>{{row.name}}</td>
</tr>
</f-repeat>Nesting
<f-when> and <f-repeat> can be nested arbitrarily:
<f-when value="{{show}}">
<f-repeat value="{{row in rows}}">
<f-repeat value="{{cell in row.cells}}">
<span>{{cell}}</span>
</f-repeat>
</f-repeat>
</f-when>Property Access, Array Indexing, and `.length`
Dot-notation, numeric indices, and the special .length property on arrays are all supported:
{{user.address.city}}
{{list.0.name}}
{{items.length}}{{items.length}} resolves to the number of elements in the array. It can be used in both content bindings and <f-when> expressions:
<f-when value="{{items.length > 0}}">
<p>{{items.length}} items found</p>
</f-when>Custom Elements
When given a Locator, the renderer detects custom elements (any tag whose name contains a hyphen, excluding f-when/f-repeat) and renders their corresponding HTML template files.
Setting up a Locator
use microsoft_fast_build::Locator;
// From glob patterns — scans the filesystem
let locator = Locator::from_patterns(&[
"./components/**/*.html",
"./templates/*.html",
])?;
// Or build manually (useful in tests)
let mut locator = Locator::from_templates(std::collections::HashMap::new());
locator.add_template("my-button", "<button>{{label}}</button>");Glob patterns support * (any characters within one path segment), ** (any number of segments), and ? (any single character).
Note:
Locator::from_patternswalks the filesystem recursively up to 50 directory levels deep and skips symlinks. Symlinked directories are never followed, guarding against cycles in the directory tree.
HTML File Format
Template HTML files must wrap their content in an <f-template> element with a name attribute identifying the custom element, and an inner <template> element containing the template markup:
<f-template name="my-button">
<template>
<button>{{label}}</button>
</template>
</f-template>A single file may contain multiple templates:
<f-template name="my-header">
<template>
<header><h1>{{title}}</h1></header>
</template>
</f-template>
<f-template name="my-footer">
<template>
<footer>{{copyright}}</footer>
</template>
</f-template>If an <f-template> element is missing a name attribute, a warning is emitted to stderr and the template is ignored. The parser follows browser tag boundaries for the <f-template> and inner <template> wrappers, including ASCII whitespace before > in opening and closing tags.
Any shadowroot* attributes on <f-template> are copied to the rendered Declarative Shadow DOM <template>. The renderer normalizes shadowrootmode and legacy shadowroot for compatibility: when neither has a non-empty value, it emits shadowrootmode="open" shadowroot="open"; when exactly one has a non-empty value, that value is mirrored to the other; when both have explicit non-empty values, both are preserved as authored, even if they conflict.
Composing CSS into an `` — `compose_f_template_styles`
compose_f_template_styles inserts a <style> element as the first child of an <f-template> definition's inner <template>, entirely in memory:
use microsoft_fast_build::compose_f_template_styles;
let template_html = r#"
<f-template name="my-button">
<template>
<button>{{label}}</button>
</template>
</f-template>
"#;
let composed = compose_f_template_styles(
template_html,
":host { display: inline-block; }",
)?;
// composed now has <style>:host { display: inline-block; }</style> as the
// first child of the inner <template>, with every other byte preserved.This has no filesystem requirement — template_html and css can come from any source (a build pipeline, a bundler plugin, generated CSS). The result is ordinary <f-template> source: write it to a file for Locator::from_patterns to discover, or pass it to locator::parse_f_templates directly.
Validation:
template_htmlmust contain exactly one<f-template>element, and that element must contain exactly one inner<template>element, or the call returns an error (MissingFTemplate,MultipleFTemplates,MissingInnerTemplate,MultipleInnerTemplates).cssmust not contain a case-insensitive</styleraw-text terminator sequence (e.g.</style>,</STYLE >,</style/) — this would prematurely close the inserted<style>element. Content likecontent: "</stylesheet>"is not affected, sincestylemust be followed by whitespace,/,>, or end of string to count as a terminator.
The WASM build exposes the same function as compose_f_template_styles(templateHtml, css), throwing a JS error on any validation failure.
Rendering with a Locator
use microsoft_fast_build::{render_with_locator, render_template_with_locator, Locator};
let locator = Locator::from_patterns(&["./components/**/*.html"])?;
let html = render_template_with_locator(
r#"<my-button label="Submit"></my-button>"#,
r#"{"disabled": false}"#,
&locator,
)?;Rendering entry HTML (root custom elements)
When rendering the top-level entry HTML of a page, use render_entry_with_locator or render_entry_template_with_locator. Custom elements found at this level are treated as root elements — their opening-tag {{binding}} attributes are resolved (primitives kept, non-primitives stripped) rather than forwarded verbatim.
All custom elements (root and nested) receive the full root state merged with their HTML attribute-derived state. Attribute-derived values take precedence over root state for overlapping keys. This gives every element access to the complete state tree without requiring explicit attribute bindings.
use microsoft_fast_build::{render_entry_with_locator, render_entry_template_with_locator, Locator};
let locator = Locator::from_patterns(&["./components/**/*.html"])?;
// Root custom elements (my-header, my-app) receive the full root state merged with
// any per-element attributes. Their templates can reference any key in the state JSON directly.
let html = render_entry_template_with_locator(
r#"{{heading}}<my-header></my-header><my-app planet="earth"></my-app>"#,
r#"{"heading": "Hello", "user": "Alice", "items": [{"name": "Item 1"}]}"#,
&locator,
)?;
// my-header's template can use {{user}}, {{heading}}, etc. directly.
// my-app's template can use {{items}}, {{planet}}, etc. directly.State Propagation
All custom elements — whether at the entry level or deeply nested — receive the current root state as a base for their child state. Per-element HTML attributes are overlaid on top, with attribute-derived values taking precedence over any matching root state keys.
This means unbound state keys automatically propagate through the entire element tree. A child element can reference any state key from its ancestors without requiring explicit attribute bindings on every intermediate element:
<f-template name="my-el">
<template>
{{text}}
<my-child-el></my-child-el>
</template>
</f-template>
<f-template name="my-child-el">
<template>
{{text}}
</template>
</f-template>Both my-el and my-child-el render "Hello world" — the state propagates through the element tree without any explicit bindings.
Attribute handling on root elements
Root custom elements (rendered via render_entry_*) have their opening-tag {{binding}} attributes resolved in the rendered HTML output:
- Primitive bindings (
string,number,boolean) — resolved and rendered with the resolved value. e.g.text="{{message}}"→text="Hello world". - Non-primitive bindings (
array,object,null) — stripped from the HTML output (the state is still available in the element's template via state propagation). - Static attributes (no binding syntax) — passed through unchanged.
| Context | Child state source | {{binding}} attrs in rendered HTML |
|---|---|---|
Root element in entry HTML (via render_entry_*) |
Root state merged with element's own attrs | Resolved — primitives kept, non-primitives stripped |
| Nested element inside a shadow template | Parent state merged with element's own attrs | Rendered (resolved) |
Element inside f-repeat or f-when (at any level) |
Parent state merged with element's own attrs | Rendered (resolved) |
<my-parent label="{{title}}" list="{{items}}" planet="earth"></my-parent>
<my-child label="{{heading}}" :items="{{items}}"></my-child>Attribute → State Mapping
Attributes on a custom element become the state passed to its template:
| Attribute form | State entry |
|---|---|
disabled (boolean, no value) |
{"disabled": true} |
label="Click me" |
{"label": "Click me"} |
count="42" |
{"count": "42"} |
items="{{items}}" |
{"items": <value of items from parent state>} (array, object, or any type) |
:items="{{items}}" |
{"items": <value of items from parent state>} — same as above but not rendered as an HTML attribute |
foo="{{bar}}" |
{"foo": <value of bar from parent state>} |
selected-user-id="42" |
{"selected-user-id": "42"} (or {"selectedUserId": "42"} with camelCase strategy) |
isEnabled="{{isEnabled}}" |
{"isenabled": <resolved value>} |
data-date-of-birth="1990-01-01" |
{"dataset": {"dateOfBirth": "1990-01-01"}} |
data-date-of-birth="{{dob}}" |
{"dataset": {"dateOfBirth": <value of dob from parent state>}} |
aria-disabled="true" |
{"ariaDisabled": "true"} |
aria-label="{{label}}" |
{"ariaLabel": <value of label from parent state>} |
tabindex="0" |
{"tabIndex": "0"} |
readonly="true" |
{"readOnly": "true"} |
contenteditable="true" |
{"contentEditable": "true"} |
:myProp="{{expr}}" |
{"myprop": <resolved value>} — not rendered as an HTML attribute |
@click="{handler()}" |
(skipped — client-side only) |
f-ref="{video}" |
(skipped — client-side only) |
f-slotted="{nodes}" |
(skipped — client-side only) |
f-children="{items}" |
(skipped — client-side only) |
HTML attribute keys are lowercased — HTML attribute names are case-insensitive and browsers always store them lowercase. isEnabled becomes isenabled; hyphens are preserved so selected-user-id stays selected-user-id. Templates must reference the lowercase form. When the attribute-name-strategy configuration is set to "camelCase", dashed attribute names that are not handled by specialized lookup tables (data-*, aria-*, HTML global attributes) are converted to camelCase instead: selected-user-id becomes selectedUserId, matching {{selectedUserId}} in the template. See the Configuration section for details.
Attribute value coercion — attribute values are resolved in this order:
- No value (boolean attribute) →
true "{{binding}}"→ resolved from parent state (any type: string, number, boolean, array, or object)- Value starting with
[or{→ parsed as a JSON array or object literal (e.g.items='["a","b","c"]') - Anything else →
String(e.g.count="42"yields{"count": "42"}; usecount="{{count}}"to get a number)
Use :prop="{{binding}}" to pass arrays and objects from state without polluting HTML attributes — the : prefix causes the attribute to be stripped from the rendered HTML while still forwarding the resolved value (which can be an array or object) into the child element's rendering state. Alternatively, JSON array or object literals can be inlined directly as attribute values (e.g. items='["a","b","c"]'), which is useful when the data is static and does not come from parent state.
data-* attributes are always grouped under a nested "dataset" key. data_attr_to_dataset_key returns the full dot-notation path (e.g. "dataset.dateOfBirth"), which is split on . when building the nested state, making {{dataset.X}} bindings work naturally in shadow templates.
aria-* attributes are converted to their camelCase ARIA property name (e.g. aria-disabled → ariaDisabled, aria-valuenow → ariaValueNow) using a static lookup table, following the Element ARIA reflection convention. Unrecognised aria-* attributes fall through to the default lowercased-key path.
HTML attributes with mismatched property names (e.g. tabindex → tabIndex, readonly → readOnly, contenteditable → contentEditable) are converted to their camelCase DOM property name using a static lookup table. Only attributes where the property name differs are included — attributes like disabled, title, and hidden pass through as-is.
@event bindings and f-ref/f-slotted/f-children directives are skipped entirely — not added to child state and removed from rendered HTML. They are resolved purely by the FAST client runtime. The data-fe binding count still includes them so the FAST runtime allocates the correct number of binding slots.
Output Format
The renderer wraps the rendered template in Declarative Shadow DOM and adds the hydration attributes required by the FAST client runtime:
<my-button label="Submit">light DOM</my-button>
<my-button label="Submit">
<template shadowrootmode="open" shadowroot="open">
<button>Submit</button>
</template>
light DOM
</my-button>shadowrootmode="open"— the standard declarative shadow DOM mode emitted by default.shadowroot="open"— legacy declarative shadow DOM attribute for broader browser compatibility.shadowroot*attributes declared on the source<f-template>are forwarded to the output<template>;shadowrootmodeandshadowrootdefault toopenwhen neither has a non-empty value, mirror when exactly one has a non-empty value, and are preserved as authored when both have explicit non-empty values.
Custom elements that have no matching template in the locator are passed through verbatim.
Configuration
The RenderConfig struct provides configuration options that control rendering behaviour. Pass Some(&config) to any render function, or None for defaults.
`attribute-name-strategy`
Controls how HTML attribute names are mapped to state property names when building child state for custom elements.
| Strategy | Behaviour | Example |
|---|---|---|
"none" (default) |
Attribute names are lowercased as-is. Dashes preserved. | foo-bar → {{foo-bar}} |
"camelCase" |
Dashed attribute names are converted to camelCase. | foo-bar → {{fooBar}} |
The camelCase strategy only applies to "plain" attributes. It does not affect:
data-*attributes (always usedataset.*grouping)aria-*attributes (always use ARIA reflection lookup)- HTML global attributes with known property names (e.g.
tabindex→tabIndex) :propproperty bindings (always lowercased)
Rust API
use microsoft_fast_build::{RenderConfig, AttributeNameStrategy, render_template_with_locator};
let config = RenderConfig::new()
.with_attribute_name_strategy(AttributeNameStrategy::CamelCase)
.with_markers_v2(true);
let result = render_template_with_locator(
r#"<my-el foo-bar="hello"></my-el>"#,
r#"{}"#,
&locator,
Some(&config),
)?;
// In my-el's template, use {{fooBar}} instead of {{foo-bar}}WASM / JavaScript API
const html = render_entry_with_templates(
entry,
templatesJson,
stateJson,
"camelCase", // or "none"
false, // optional stream flag
true // optional markers_v2 flag
);
const chunksJson = render_entry_with_templates(
entry,
templatesJson,
stateJson,
"camelCase",
true,
true
);Use render_with_templates for the original non-entry template-rendering semantics; use render_entry_with_templates for top-level entry HTML rendering. Passing true as the optional fifth argument switches render_entry_with_templates to stream mode and returns a JSON array string of HTML chunks; omitted or false returns normal HTML. Passing true as the optional sixth markers_v2 argument emits FAST Element 2.x indexed hydration markers.
Passing "none" or "" as the strategy uses the default behaviour.
Hydration Markers
When a custom element's shadow template is rendered, the renderer emits hydration markers so the FAST client runtime can efficiently locate and patch DOM nodes without a full diff.
FAST Element 3.x data-free markers are emitted by default. SetRenderConfig::with_markers_v2(true) in Rust, or pass the markers_v2 WASM/CLI
option, to emit FAST Element 2.x indexed markers for a client configured withenableHydration({ markers: markers_v2 }).
Content binding markers
Each {{expr}} or {{{expr}}} text binding is wrapped in data-free HTML comment markers:
Hello worldMarkers carry no binding index or expression name. They are matched by string equality and paired using balanced depth counting.
Attribute binding markers
Elements with {{expr}} attribute values, ?attr="{{expr}}" boolean bindings, or {expr} single-brace event/directive bindings receive a data-fe="N" marker attribute where N is the binding count. Client-only attributes (@event, :property, f-ref, f-slotted, f-children) are stripped from the HTML output but still counted in the marker:
<input type="checkbox" disabled data-fe="1">
<input disabled data-fe="1">
<input data-fe="1">
<button data-fe="1">Label</button>
<slot data-fe="1"></slot>
<video class="my-video" data-fe="2">data-fe="N" — N is the total number of attribute bindings on the element.
Scope boundaries
Each custom element shadow, <f-when> body, and <f-repeat> item template gets its own scope with the binding index reset to 0. Scopes don't carry numeric IDs — markers are data-free and paired by balanced depth counting.
Directive markers
[inner content, or empty if condition is false]
[item 0 — binding index reset to 0 per item]
[item 1 — binding index reset to 0 per item]Error Handling
Render functions return Result<_, RenderError> (HTML or stream chunks).RenderError exposes code(), location() -> Option<ErrorLocation>, andtemplate_name(). ErrorLocation has one-based line and UTF-16 column, plus
a UTF-16 length. Match error.kind() to inspect variants through source-location
wrappers. WASM failures are JavaScript Error instances with these details as
properties, not plain strings. See the source-coordinate contract and migration
notes.
| Variant | Code | Triggered by |
|---|---|---|
InvalidTemplateMetadata |
invalid-template-metadata |
Unsupported version, missing name, or duplicate metadata name |
UnknownTemplate |
missing-template |
Shadow-root render requests an unknown definition |
UnclosedBinding |
unclosed-binding |
{{ with no closing }} |
UnclosedUnescapedBinding |
unclosed-unescaped-binding |
{{{ with no closing }}} |
EmptyBinding |
empty-binding |
Blank binding expression |
MissingState |
missing-state |
Required directive state is absent; missing repeat lists still render zero iterations |
UnclosedDirective |
unclosed-directive |
Directive/custom element with no matching close tag |
MissingValueAttribute |
missing-value-attribute |
Directive missing a valid value="{{…}}" |
InvalidRepeatExpression |
invalid-repeat-expression |
Repeat value not in item in list format |
NotAnArray |
repeat-not-array |
Repeat binding resolves to a non-array |
DuplicateTemplate |
duplicate-template |
Multiple files define the same name |
TemplateReadError |
template-read-error |
A matched template file could not be read |
JsonParse |
invalid-state-json |
Invalid JSON state |
MissingFTemplate |
missing-f-template |
Composition input has no outer wrapper |
MultipleFTemplates |
multiple-f-templates |
Composition input has multiple outer wrappers |
MissingInnerTemplate |
missing-inner-template |
Composition input has no inner template |
MultipleInnerTemplates |
multiple-inner-templates |
Composition input has multiple inner templates |
UnsafeStyleContent |
unsafe-style-content |
Separately supplied CSS contains a </style terminator |
Located preserves its underlying variant's code/message. WASM template-map JSON
and attribute-record validation also use invalid-template-metadata; an invalid
WASM attribute-name strategy uses invalid-attribute-name-strategy. These failures
do not acquire HTML coordinates.
Messages retain the existing explanation and include a snippet when available:
type error: '{{items}}' must resolve to a JSON array for use in <f-repeat> — template: "…<f-repeat value=\"{{item in items}}\">…"
unclosed binding '{{name': no closing '}}' found to end the expression — template: "Hello {{name"
duplicate template: element '<my-button>' is defined in multiple files: ./a/my-button.html, ./b/my-button.htmlNumeric Precision
The hand-rolled JSON parser stores all numbers as f64. This means integers larger than 2^53 (9,007,199,254,740,992) lose precision, as is the case with standard JavaScript number values. For typical use cases (IDs, counts, display values) this is not a concern. If your state contains very large integers, represent them as strings and bind with {{expr}} accordingly.
Renderer performance
Tag and binding discovery uses a forward event scan rather than repeated
remaining-suffix searches for each directive kind. Adjacent custom elements
and plain tags no longer incur those quadratic searches. Rendered HTML,
attribute handling, default markers, and markers_v2 remain compatible.
Templates still render recursively; this is not a compiled-template or lazy
streaming API. Stream functions continue to return fully materialized chunks.
From a FAST repository checkout, run the native benchmarks and parity tests:
cargo bench --manifest-path crates/microsoft-fast-build/Cargo.toml --bench render
cargo test --manifest-path crates/microsoft-fast-build/Cargo.tomlCriterion separates parsed-state rendering, state parsing plus rendering, and
locator construction. Shared fixtures cover 1k/5k/20k instances and
50/200/1,000 definitions with payload sizes 1/20 in both marker modes.
See performance baselines for the packaged
WASM runner, golden hashes, scaling checks, RSS measurement, and limitations.
The guide also records post-scanner repeat/state-overlay and many-definition
profiles, with bounded workload commands and symbolized native CPU sampling
through Criterion's --profile-time mode. Those measurements identify child-state
copying as the next repeat/overlay target; they do not claim that memory or
allocation targets have been met.
For native allocation baselines, use the opt-in development example or the
fresh-process matrix runner from the repository root:
node crates/microsoft-fast-build/benches/profile-allocations.mjs --output=/tmp/fast-allocations.json
cargo run --manifest-path crates/microsoft-fast-build/Cargo.toml --release --features allocation-profiling --example profile-allocations -- --case=repeat_custom_elements --count=250 --mode=v3 --phase=renderThe feature does not instrument the library or shipped WASM. Reports count
only allocations on the measuring thread within the selected phase, excluding
prepared inputs and report generation. See the baseline guide for controls,
output retention, reallocation semantics, and test commands.
Overview (`crates/microsoft-fast-convert/README.md`)
microsoft-fast-convert
microsoft-fast-convert converts one FAST declarative HTML template string into another supported syntax. It is written in Rust and exposes both a Rust API and a wasm-bindgen API for Node tooling.
Supported targets
webui-prerelease— unwraps the outer<f-template>and emits HTML for WebUI's FAST parser plugin, converting only<f-repeat>to<for>and<f-when>to ``. Other supported FAST attributes (@click,:prop,?bool,f-ref,f-children,f-slotted) pass through unchanged.webui-framework-prerelease— converts FAST's declarative syntax into the Microsoft WebUI Framework's own declarative runtime syntax (transferring Shadow/Light DOM policy from the outer<f-template>onto the inner<template>). It converts<f-repeat>to<for>,<f-when>to ``,f-refto a bracedw-ref, and FAST's$eevent argument to baree.f-slotted/f-childrenare stripped from the output with a warning, since WebUI Framework has no equivalent; every other unsupported construct (unknownf-*,$cevent context, non-bracedf-ref, conflicting shadow-root modes) remains a hard error. The target's name reflects the WebUI Framework project's own current prerelease status, not the stability of this target's implementation.fast-v3-ts— emits TypeScript source that imports FAST helpers and exportstemplateas anhtmltagged template.
Rust usage
use microsoft_fast_convert::{convert_template, convert_stylesheet};
let source = r#"<f-template name="my-element"><template>{{title}}</template></f-template>"#;
let result = convert_template(source, "fast-v3-ts")?;
let ts = result.output;
let css = ":host { display: block; }";
let styles = convert_stylesheet(css, "fast-v3-ts", "styles")?;convert_template/convert_template_with_options returnResult<ConvertOutput, ConvertError>, where ConvertOutput { output: String, warnings: Vec<ConvertWarning> }. warnings reports non-fatal issues — such as a
stripped f-slotted/f-children directive, produced only bywebui-framework-prerelease — that didn't stop conversion;webui-prerelease and fast-v3-ts output always have an empty warnings
vector, since neither strips anything. convert_stylesheet is unaffected and
still returns Result<String, ConvertError>.
convert_stylesheet(stylesheet, syntax, export_name) converts a CSS source string
into a TypeScript module that imports css from @microsoft/fast-element and
exports export_name as a css tagged template. It has no filesystem
requirement and, like convert_template, is only supported for thefast-v3-ts syntax. export_name must be a valid TypeScript/JavaScript
identifier (no dotted paths).
fast-v3-ts output can optionally include an explicit TSource generic parameter
(html<TypeSource> instead of bare html) via convert_template_with_options andConvertOptions:
use microsoft_fast_convert::{convert_template_with_options, ConvertOptions};
let options = ConvertOptions {
type_source: Some("MyElement".to_string()),
type_source_import: Some("./my-element.js".to_string()),
..ConvertOptions::default()
};
let ts = convert_template_with_options(source, "fast-v3-ts", &options)?.output;type_source must be a dotted TypeScript identifier (e.g. MyElement orNamespace.MyElement); type_source_import, when provided, also emits a matchingimport type { <root_identifier> } from "<type_source_import>"; statement. type_source
is only supported for fast-v3-ts; type_source_import requires type_source.
Syntax metadata is available through syntax_metadata(). The metadata includes each
supported syntax name, required output extension, and default output suffix so package
tooling can avoid duplicating target definitions.
Explicit public export types
Set ConvertOptions { typed: true, ..ConvertOptions::default() } forfast-v3-ts template conversion. It emits an ElementViewTemplate<TSource>
annotation (or ElementViewTemplate when no source type is supplied) and a
type-only FAST import. The default remains byte-compatible unannotated output.
Other syntax targets reject typed: true.
convert_stylesheet_with_options(css, syntax, export_name, &options) similarly
emits export const styles: ElementStyles = css... with a type-only import.
Stylesheet options reject type_source/type_source_import. Existingconvert_stylesheet calls keep their original output. Type imports use the
root of dotted source names; FAST type names are aliased when necessary to
avoid collisions with supplied names.
Typed exports support isolatedDeclarations; the converter does not infer a
state interface. Supply a concrete source type for strict property validation.
Native struct literals should use ..ConvertOptions::default() for unset options.
WASM usage
When built with wasm-pack --target nodejs, the crate exports:
interface ConvertResult {
readonly output: string;
readonly warnings: string[];
}
convert_template(template: string, syntax: string): ConvertResult
convert_template_with_options(
template: string,
syntax: string,
typeSource?: string,
typeSourceImport?: string,
typed?: boolean,
): ConvertResult
convert_stylesheet(stylesheet: string, syntax: string, export_name: string, typed?: boolean): string
convert_syntax_metadata(): stringconvert_syntax_metadata() returns JSON metadata for supported syntax targets.
Errors are JavaScript Error instances, retaining the ConvertError message and
adding code plus optional line, column, length, and templateName.
Generated declarations export the FastBuildError extends Error interface.
Coordinates refer to the complete original input, not the extracted inner
template or rewritten output. They use one-based UTF-16 columns/lines and a UTF-16
error-token highlight length; CRLF is one break and lone CR/LF also break lines.
Options/syntax failures have no HTML coordinates.
Native ConvertError provides code(), location() -> Option<ErrorLocation>,template_name(), and kind(); use kind() when matching source-wrapped
validation errors. Context fields are now boxed ErrorContext values. See the
migration guide.
Error codes
| Code | Failure |
|---|---|
unsupported-syntax |
Unknown syntax target |
typed-unsupported-for-syntax |
Typed output requested for non-TypeScript syntax |
type-source-unsupported-for-stylesheet |
Source-type options supplied for CSS |
stylesheet-unsupported-for-syntax |
CSS requested for a target other than fast-v3-ts |
invalid-export-name |
Invalid stylesheet export identifier |
type-source-unsupported-for-syntax |
Source-type options supplied for a non-TypeScript target |
invalid-type-source |
Invalid dotted source-type identifier |
type-source-import-requires-type-source |
Import module supplied without a source type |
missing-f-template |
No outer wrapper |
multiple-f-templates |
Multiple outer wrappers |
missing-f-template-name |
Outer wrapper lacks a string name |
empty-f-template-name |
Outer wrapper name is blank |
missing-inner-template |
No inner template |
multiple-inner-templates |
Multiple inner templates |
unclosed-element |
No matching closing element |
unclosed-tag |
Opening tag has no terminating > |
missing-value-attribute |
Directive lacks its required value |
invalid-directive-value |
Directive value lacks double-brace delimiters |
invalid-repeat-expression |
Repeat value is not item in items |
unclosed-binding |
Binding lacks closing delimiters |
empty-binding |
Binding expression is blank |
unsupported-f-attribute |
Unsupported FAST attribute |
unsupported-f-element |
Unsupported FAST element |
unsupported-expression |
Expression is outside the converter grammar |
unsupported-event-handler |
Event handler cannot be converted |
unsupported-event-context |
FAST context token has no WebUI mapping |
conflicting-shadow-root-mode |
Outer and inner shadow-root declarations conflict |
unsupported-publisher-attribute |
Outer attribute cannot be transferred |
invalid-attribute-value |
Attribute lacks its required braced form |
Validation
The converter validates that the input contains exactly one <f-template name="…"> with a non-empty name and exactly one inner <template>. It validates supported syntax values, <f-repeat> and <f-when> value="{{…}}" expressions, supported f-* attributes, and the limited expression grammar used for fast-v3-ts output. It also validates that type_source is a dotted identifier, that type_source is only supplied for fast-v3-ts, and that type_source_import is only supplied alongside type_source.
The webui-framework-prerelease target additionally validates that outer <f-template> attributes (other than name) are shadowroot-prefixed (ConvertError::UnsupportedPublisherAttribute otherwise), that a shadowroot-prefixed outer attribute doesn't conflict with a differently-valued attribute already on the inner <template> (ConvertError::ConflictingShadowRootMode), that f-ref values are single-braced (ConvertError::InvalidAttributeValue), and that event arguments don't use FAST's repeat-context tokens such as $c (ConvertError::UnsupportedEventContext). f-slotted and f-children are the one exception to this target's otherwise strict validation: rather than erroring, they are stripped from the output and reported via ConvertOutput::warnings as a ConvertWarning::StrippedUnsupportedDirective.
convert_stylesheet validates that syntax is fast-v3-ts (CSS conversion is not supported for webui-prerelease/webui-framework-prerelease) and that export_name is a valid TypeScript/JavaScript identifier. Stylesheet content is escaped for safe embedding in a TypeScript template literal: backslashes, backticks, literal ${ sequences, and carriage returns are all escaped.
Overview (`examples/design-system/README.md`)
@microsoft/fast-examples-design-system
A CSS-only design system for FAST example apps. The package ships three
hand-authored stylesheets that define a shared set of CSS custom properties
for color, typography, spacing, shape, elevation, and motion. There is no
JavaScript or TypeScript implementation — example apps wire themes
themselves by toggling the data-theme attribute on <html>.
This README and DESIGN.md are the canonical guidance for both
humans and coding agents working in the examples/* workspace.
Token names are semantic and use-site-oriented, with the --fast- prefix
and coverage tuned for FAST example apps.
What ships
The package ships exactly one stylesheet:
| File | Contents |
|---|---|
tokens.css |
Every token, declared once on :root. Color and elevation values use the CSS light-dark() function, so each declaration carries both its light and dark resolution side by side. color-scheme: light dark enables system-preference resolution; <html data-theme="light"> or <html data-theme="dark"> forces a theme. |
The package is "private": true and consumed via the workspace dependency"@microsoft/fast-examples-design-system": "workspace:*".
tokens.css uses the CSS light-dark() function, which requires
Chrome / Edge 123+, Firefox 120+, or Safari 17.5+ (all shipped in
2023–2024). These example apps target evergreen browsers; there is no
fallback for older versions.
Install
Add the workspace dependency in the consuming example app's package.json,
then run npm install from the repository root.
{
"dependencies": {
"@microsoft/fast-examples-design-system": "workspace:*"
}
}Use
Import the stylesheet exactly once at the app entry point — typically the same
file that defines or bootstraps the root custom element. The bundler (Vite in
the existing examples) inlines the CSS into the document.
// src/main.ts
import "@microsoft/fast-examples-design-system/tokens.css";<!doctype html>
<html lang="en">
<body>
<my-app></my-app>
</body>
</html>If the app is intentionally single-theme, hard-code the attribute and never
touch it from JavaScript:
<html lang="en" data-theme="light">Referencing tokens from component CSS
Reference tokens through var(--fast-...) in FAST css template literals or
in any other CSS context. Never hard-code colors, font sizes, spacing, radii,
border widths, shadows, durations, or curves.
import { css } from "@microsoft/fast-element";
export const styles = css`
:host {
display: block;
padding: var(--fast-padding-content-medium);
background: var(--fast-background-layer-primary-solid);
color: var(--fast-foreground-ctrl-neutral-primary-rest);
border: var(--fast-stroke-width-default) solid
var(--fast-stroke-divider-subtle);
border-radius: var(--fast-corner-large);
box-shadow: var(--fast-shadow-card-rest);
font-family: var(--fast-text-style-default-regular-font-family);
font-size: var(--fast-text-global-body3-font-size);
line-height: var(--fast-text-global-body3-line-height);
transition: background var(--fast-duration-fast)
var(--fast-curve-easy-ease);
}
`;Theme switching
The active theme is selected by the data-theme attribute on <html>:
data-theme value |
Effective theme |
|---|---|
"light" |
Forced light |
"dark" |
Forced dark |
| absent | Follows prefers-color-scheme (dark if the system requests dark, light otherwise) |
Under the hood, tokens.css declares color-scheme: light dark on :root
and uses the CSS light-dark() function in every color/elevation token.
The browser picks the matching value based on the inheritedcolor-scheme. Setting data-theme="light" or data-theme="dark"
overrides color-scheme to that single value, which flips everylight-dark() resolution. No media query is needed.
To wire a toggle, write the attribute directly from your application code. The
design system itself contains no JavaScript — toggles, persistence, and event
plumbing are intentionally left to the consuming app so the system stays
framework-neutral and dependency-free.
// Force light
document.documentElement.setAttribute("data-theme", "light");
// Force dark
document.documentElement.setAttribute("data-theme", "dark");
// Restore system preference
document.documentElement.removeAttribute("data-theme");See ../todo-app/ for a working consumer of the design
system. The example deliberately uses a single (light) theme set
statically in markup; it does not implement a runtime toggle.
Token catalog
The complete list of tokens lives in tokens.css. Categories
at a glance:
| Category | Examples |
|---|---|
| Background: surfaces | --fast-background-web-page-primary, --fast-background-layer-primary-solid, --fast-background-layer-secondary |
| Background: subtle controls | --fast-background-ctrl-subtle-rest, --fast-background-ctrl-subtle-hover, --fast-background-ctrl-subtle-pressed, --fast-background-ctrl-subtle-disabled |
| Background: brand controls | --fast-background-ctrl-brand-rest, --fast-background-ctrl-brand-hover, --fast-background-ctrl-brand-pressed, --fast-background-ctrl-brand-disabled |
| Foreground | --fast-foreground-ctrl-neutral-primary-rest, --fast-foreground-ctrl-neutral-primary-disabled, --fast-foreground-ctrl-neutral-secondary-rest, --fast-foreground-ctrl-hint-default, --fast-foreground-ctrl-on-brand-rest, --fast-foreground-ctrl-brand-rest |
| Stroke: dividers | --fast-stroke-divider-default, --fast-stroke-divider-subtle, --fast-stroke-divider-strong, --fast-stroke-divider-brand |
| Stroke: control outlines | --fast-stroke-ctrl-on-outline-rest, --fast-stroke-ctrl-on-outline-disabled |
| Stroke widths | --fast-stroke-width-default, --fast-stroke-width-divider-strong |
| Control focus | --fast-ctrl-focus-outer-stroke, --fast-ctrl-focus-outer-stroke-width |
| Status: solid | --fast-status-{danger, success, warning}-background, --fast-status-{danger, success, warning}-foreground |
| Status: tinted | --fast-status-{danger, success, warning}-tint-background, --fast-status-{danger, success, warning}-tint-foreground, --fast-status-{danger, success, warning}-tint-stroke |
| Shadow | --fast-shadow-card-rest-key, --fast-shadow-card-rest-ambient, --fast-shadow-card-rest, --fast-shadow-flyout-key, --fast-shadow-flyout-ambient, --fast-shadow-flyout |
| Text: families | --fast-text-style-default-regular-font-family, --fast-text-style-code-regular-font-family, --fast-text-style-data-viz-regular-font-family |
| Text: weights | --fast-text-style-default-regular-weight, --fast-text-style-default-header-weight, --fast-text-style-default-display-weight |
| Text: global ramp (font size + line height pairs) | --fast-text-global-caption2-*, --fast-text-global-caption1-*, --fast-text-global-body3-*, --fast-text-global-body2-*, --fast-text-global-body1-*, --fast-text-global-subtitle2-*, --fast-text-global-subtitle1-*, --fast-text-global-title2-*, --fast-text-global-title1-*, --fast-text-global-display2-*, --fast-text-global-display1-* |
| Padding (inset) | --fast-padding-content-none, --fast-padding-content-xx-small, -x-small, -small, -medium, -large, -x-large, -xx-large, -xxx-large |
| Gap (between elements) | --fast-gap-between-content-none, --fast-gap-between-content-xx-small, -x-small, -small, -medium, -large, -x-large, -xx-large |
| Corner | --fast-corner-none, --fast-corner-small, --fast-corner-medium, --fast-corner-large, --fast-corner-circular |
| Duration | --fast-duration-ultra-fast, --fast-duration-faster, --fast-duration-fast, --fast-duration-normal, --fast-duration-gentle, --fast-duration-slow, --fast-duration-slower, --fast-duration-ultra-slow |
| Curve | --fast-curve-accelerate-max, -mid, -min, --fast-curve-decelerate-max, -mid, -min, --fast-curve-easy-ease-max, --fast-curve-easy-ease, --fast-curve-linear |
State vocabulary on interactive tokens: rest, hover, pressed,disabled. Optionally prefixed with selected- for selectable controls.
See DESIGN.md for the full naming grammar, the rationale, and
the rules for adding new tokens.
Overview (`examples/test/README.md`)
FAST Examples — End-to-End Tests
Playwright end-to-end tests that exercise the three Todo example apps in this monorepo as black-box web applications. The same test suite runs against each app, asserting that the unified UI and functionality behave identically regardless of implementation.
| App | Project name | Dev server |
|---|---|---|
examples/csr/todo-app |
csr-todo |
vite on port 9000 |
examples/csr/todo-mobx-app |
csr-todo-mobx |
vite on port 9001 |
examples/ssr/webui-todo-app |
ssr-webui-todo |
webui serve on port 8081 |
Each project is configured in playwright.config.ts with its own webServer block — Playwright starts and stops them automatically.
Running
From the monorepo root:
# install deps and browsers (once)
npm ci
npx playwright install --with-deps chromium
# build the example apps the tests depend on
npm run build -w @microsoft/fast-element
npm run build -w @microsoft/fast-todo-app-example
npm run build -w @microsoft/fast-todo-mobx-app-example
npm run build -w @microsoft/fast-webui-todo-app-example
# run all three suites
npm run test:e2eTo run a single app's suite:
npm run test:e2e -w @microsoft/fast-examples-e2e -- --project=csr-todo
npm run test:e2e -w @microsoft/fast-examples-e2e -- --project=csr-todo-mobx
npm run test:e2e -w @microsoft/fast-examples-e2e -- --project=ssr-webui-todoLayout
examples/test/
├── package.json
├── playwright.config.ts
├── DESIGN.md // design notes for contributors
├── support/
│ ├── fixtures.ts // injects the per-project adapter
│ ├── global-setup.ts
│ ├── global-teardown.ts
│ └── adapters/
│ ├── index.ts // resolves the adapter for a project
│ ├── types.ts // TodoAppAdapter interface
│ ├── csr.ts // shared adapter for both CSR apps
│ └── ssr-webui-todo.ts
└── tests/
└── todo.pw.spec.ts // shared suite, runs against every projectAdapters wrap the per-app DOM differences (e.g. SSR app uses a nested <todo-item> shadow root per row, while CSR apps render inline <li> content) behind a uniform interface (addTodo, descriptions, toggleByDescription, deleteByDescription, setFilter, getCounts, …). Test specs use that interface only — no app-specific selectors leak into the suite.
Spec files use the *.pw.spec.ts suffix so Playwright picks them up while leaving the *.spec.ts suffix free for future Node-based tests.
CI
The test:e2e script is run as a separate step in .github/workflows/ci-validate-pr.yml after unit tests, gated on the same npx playwright install --with-deps invocation that already runs there.
Overview (`packages/fast-build/README.md`)
FAST Build
The fast-build package renders the declarative HTML template syntax of @microsoft/fast-element, primarily for use in testing environments. It is powered by a WebAssembly core with zero runtime npm dependencies.
Note: For production server-side rendering, we recommend using the
@microsoft/webuiproject instead.
Installation
From NPM
To install the latest @microsoft/fast-build package using npm:
npm install --save @microsoft/fast-buildWithin your JavaScript or TypeScript code, you can then import library APIs like this:
import { render } from '@microsoft/fast-build';CLI usage
Once installed, the fast binary is available. Use the build subcommand to render an HTML template with an optional JSON state file, or convert to convert a FAST declarative HTML template to another supported syntax:
fast build [options]
fast convert [options]Build options
| Option | Default | Description |
|---|---|---|
--entry="" |
index.html |
Entry HTML template to render |
--state="" |
{} when omitted |
JSON file containing the template state. If omitted, no state file is loaded and rendering uses an empty state object. If --state is provided, the file must exist. |
--output="" |
output.html |
Where to write the rendered HTML |
--templates="<glob>" |
(none) | Glob pattern(s) for custom element template HTML files. Separate multiple patterns with commas. A warning is printed if not provided in non-streaming mode or if no files match a pattern. |
--attribute-name-strategy="<strategy>" |
camelCase |
Strategy for mapping HTML attribute names to state property names on custom elements. "camelCase" converts dashes to camelCase (e.g. foo-bar → fooBar). "none" preserves dashes (e.g. foo-bar → foo-bar). See Attribute name strategy. |
--markers_v2[=true/false] |
false |
Emit FAST Element 2.x indexed hydration markers instead of the default FAST Element 3.x data-free markers. |
--config="" |
fast-build.config.json |
Path to a JSON configuration file. If omitted, fast-build.config.json in the current directory is used when present. CLI arguments take precedence over config values. See Configuration file. |
--stream[=true/false] |
false |
Write rendered HTML chunks directly to stdout instead of writing --output. Valueless --stream is treated as true. |
Example
Given an index.html:
<html>
<body>
<h1>{{title}}</h1>
<p>{{description}}</p>
</body>
</html>And a state.json:
{
"title": "Hello FAST",
"description": "Server-side rendered with WebAssembly."
}Run:
fast build --entry=index.html --state=state.json --output=output.htmlProduces output.html:
<html>
<body>
<h1>Hello FAST</h1>
<p>Server-side rendered with WebAssembly.</p>
</body>
</html>Streaming output
Pass --stream, --stream=true, or --stream=false, or set"stream": true in fast-build.config.json, to control stdout streaming. When
streaming is enabled, the CLI writes raw rendered HTML chunks to stdout, does
not write the --output file, and does not print the normal Built: ...
message.
fast build --entry=index.html --state=state.json --streamStreaming is simulated by the WebAssembly renderer: chunks are prepared in
memory, returned to JavaScript as a JSON array, parsed by the CLI, and then
written to stdout. --stream uses the same entry renderer with its stream flag
enabled and passes an empty templates map when no --templates glob is
provided. Chunk boundaries are safe for HTML parsing:
- Attribute bindings stay within complete opening tag chunks.
- Custom element opening chunks include the complete Declarative Shadow DOM
template. - Streamed output uses the same renderer preprocessing and custom-element host
attribute propagation as normal output. - Empty chunks are omitted.
- Concatenating all chunks matches the normal non-streamed render.
Performance benchmarks for contributors
The renderer discovers tags and bindings in a forward scan, eliminating repeated
remaining-suffix searches for adjacent custom elements and plain tags. Existing
WASM exports, rendered bytes, default markers, and markers_v2 remain compatible.
Rendering is still synchronous, and --stream still buffers all chunks in WASM.
From a FAST repository checkout, not an installed npm package:
npm run build -w @microsoft/fast-build
npm run bench:render -w @microsoft/fast-build -- --case=url_sink_siblings --output=/tmp/fast-render.json
npm run bench:render -w @microsoft/fast-build -- --case=url_sink_siblings --check-scalingThe runner benchmarks the actual packaged WASM in fresh child processes in both
marker modes, recording timing, CPU, output hashes, throughput, and peak RSS.
Use --case, --counts, --samples, or --compare= to narrow
or compare a run. Output files are never overwritten. Normal tests verify the
same fixtures against pre-optimization hashes without flaky timing thresholds.
See the benchmark guide
for native Criterion benchmarks, fixture sizes, methodology, and limitations.
The guide includes post-scanner repeat/state-heavy and many-definition profiles.
Start expensive repeat cases with --counts=250,1000 --warmup-count=250;
state-overlay controls and separate native phases help identify the next
optimization without running an unbounded matrix. Native CPU samples are not
WASM allocation measurements, and no production rendering behavior is changed.
The guide also includes an opt-in native allocation baseline and a fresh-process
matrix command: node crates/microsoft-fast-build/benches/profile-allocations.mjs --output=/tmp/fast-allocations.json from the repository root. Its counters are
development-only Rust tooling, not part of the npm CLI or WASM; native peak
newly-live heap is not process RSS.
Missing or omitted state
State is optional. When --state is omitted and no state path is provided by config, fast build does not look for state.json; rendering uses an empty object ({}). An explicit --state path, or a state path from config, must exist or the command exits with an error.
Breaking change: Earlier versions implicitly loaded
state.jsonfrom the current working directory when--statewas omitted. To use a state file, pass--state=state.jsonor set"state": "state.json"infast-build.config.json.
When a binding value is not present in state:
- Content bindings render nothing, including unresolved dot paths:
{{foo.bar}}becomes ``. - Attribute bindings omit the entire attribute, including unresolved dot paths:
becomes. <f-repeat>treats a missing list binding, including unresolved dot paths, as an empty array and renders zero iterations. If the binding is present but is not an array, rendering still errors.
Explicit null also omits an attribute, even in an interpolated attribute
value. Empty strings remain present. Known HTML boolean attributes bound to
JSON booleans use presence/absence; ordinary attributes (including ARIA)
retain textual booleans. Use ?name for browser-side truthy/falsy bindings.
Author bindings resolving to absence still override template-host defaults.
These intentional output changes are described in the
plugin migration guide.
APIs for build-time plugins
Import the actual exported WASM subpath; do not slice rendered host HTML:
import {
parse_f_templates,
render_shadow_root,
} from "@microsoft/fast-build/wasm/microsoft_fast_build.js";
const definitions = JSON.parse(parse_f_templates(templateHtml));
const templates = Object.fromEntries(
definitions.filter(definition => definition.name !== null)
.map(definition => [definition.name, definition]),
);
const shadowRootHtml = render_shadow_root(
"my-card", JSON.stringify(templates), JSON.stringify(componentState),
);The result is the complete DSD <template> fragment, with hydration markers
and nested components, but no host, host defaults, or light DOM. Pass resolved
component state; the API does not derive it from caller-owned host attributes.
It accepts the existing attribute-name strategy and v2 marker options.
Parsed records add formatVersion: 1 and bindings without changing the array
shape. Binding records expose name (null for compound expressions),expression, syntax kind, lexical paths, and outer-to-inner repeatscope frames. type is null except for repeat collections ("array");optional is null because required state declarations are not inferable.$index paths refer to the active repeat frame, not a root state key.
Client expressions are described lexically, not fully JavaScript-type-checked.
Code samples are excluded; metadata is not full syntax or state validation.
Generated WASM declarations expose these JSON interfaces.
Authored binding source spans
Each binding occurrence includes span: { start, end }. Both positions containoffset, line, and column, relative to the complete HTML string passed toparse_f_templates, not the extracted or trimmed template body. Duplicate
expressions have separate spans, including occurrences in host attributes,
interpolated attribute values, and nested directives.
offsetis a zero-based UTF-16 code-unit offset, compatible with JavaScript
string slicing. It is not a UTF-8 byte offset.lineandcolumnare one-based. Columns count UTF-16 code units, not visual
width or Unicode code points; a tab counts as one and a non-BMP character
counts as two.- Ranges are half-open:
startis inclusive andendis exclusive. They cover
the trimmed expression, excluding braces and surrounding whitespace. - CRLF counts as one line break but two offset units. Lone CR and LF each count
as one line break. Input line endings are not normalized. - Repeat bindings locate the iterable expression, not the alias or
inkeyword,
matching their existingexpressionfield. Nestedscopeframes are unchanged.
const [definition] = JSON.parse(parse_f_templates(templateHtml));
for (const binding of definition.bindings) {
const { start, end } = binding.span;
const authoredExpression = templateHtml.slice(start.offset, end.offset);
console.log(`${start.line}:${start.column}`, authoredExpression);
}Positions refer to the original, entity-encoded attribute text. No entity decoding
is introduced. Code samples remain excluded without shifting subsequent spans.
Callers associate the input with its file name and must regenerate metadata after
editing that input. Bindings and directives inside HTML comments remain unsupported.
Render exports reject unsupported/malformed explicit versions before rendering.
Legacy unversioned maps and string definitions remain supported. The CLI keeps
version/binding fields when assembling a map; persisted metadata should do the
same instead of discarding them.
Structured errors
Failing render, conversion, composition, and metadata-validation APIs throw actual
JavaScript Error objects, retaining the existing human-readable message.
Both WASM modules export this TypeScript interface (not a runtime constructor):
interface FastBuildError extends Error {
code: string;
line?: number;
column?: number;
length?: number;
templateName?: string;
}Use code, such as unclosed-binding, repeat-not-array, or missing-template,
instead of parsing messages. The complete code lists are documented with the
renderer errors
and converter errors.
Locations use one-based lines and UTF-16 columns, with a UTF-16 highlight length.
Tabs count as one unit, non-BMP characters as two; CRLF is one line break and lone
CR/LF are also line breaks. A highlight covers the offending opening tag or
binding including its delimiters; an unclosed binding highlights its opening
delimiters. Unlike successful binding metadata, these are error-token ranges,
not trimmed expression ranges.
Entry-render and conversion coordinates refer to the original HTML argument,
even after code-sample escaping, inner-template extraction, or publisher-attribute
rewriting. Light DOM remains in its caller's source. Errors in separately supplied
custom-element definitions include templateName and use coordinates within that
definition's supplied content, not the entry HTML or the full file it was
extracted from. Maintain a name-to-source mapping when presenting these locations.
For a missing inner template, the highlight identifies its containing<f-template>; duplicate-wrapper errors identify the second wrapper.
Coordinates are absent when there is no meaningful HTML position: invalid state
JSON, malformed metadata/options, missing template definitions or outer wrappers,
filesystem errors, and unsafe separately supplied CSS. A known templateName
can still be present without coordinates. parse_f_templates remains permissive:
it does not gain strict syntax validation or new failures.
For a direct render(html, stateJson) call, an integration can report:
try {
render(html, stateJson);
} catch (error) {
if (!(error instanceof Error)) throw error;
const location = error.line === undefined
? inputPath
: `${inputPath}:${error.line}:${error.column}`;
console.error(`${location} [${error.code}]: ${error.message}`);
}See the migration guide
when updating consumers that previously caught strings.
Custom element templates
Pass a glob pattern (or comma-separated list of patterns) to --templates to expand custom elements into Declarative Shadow DOM:
fast build \
--templates="./components/**/*.html" \
--entry=index.html \
--state=state.json \
--output=output.htmlEach matched .html file is scanned for <f-template> elements. The name attribute of each <f-template> determines the custom element name. A single file may contain multiple templates.
Template files must use the following format:
<f-template name="my-button">
<template>
<button>{{label}}</button>
</template>
</f-template>If an <f-template> element has no name attribute, a warning is printed and it is ignored. Exact file paths (no wildcards) are also accepted as patterns, making it possible to register a single template file. The parser follows browser tag boundaries for the <f-template> and inner <template> wrappers, including ASCII whitespace before > in opening and closing tags.
Any shadowroot* attributes on <f-template> are forwarded to the rendered Declarative Shadow DOM <template>. The CLI normalizes shadowrootmode and legacy shadowroot for compatibility: when neither has a non-empty value, it emits shadowrootmode="open" shadowroot="open"; when exactly one has a non-empty value, that value is mirrored to the other; when both have explicit non-empty values, both are preserved as authored, even if they conflict.
Attribute name strategy
The --attribute-name-strategy option controls how HTML attribute names on custom elements are mapped to state property names in their shadow templates.
| Strategy | Behaviour | Template binding |
|---|---|---|
camelCase (default) |
Dashed attribute names converted to camelCase | foo-bar → {{fooBar}} |
none |
Attribute names lowercased as-is, dashes preserved | foo-bar → {{foo-bar}} |
The camelCase strategy only affects "plain" custom element attributes. It does not change:
data-*attributes (always usedataset.*grouping)aria-*attributes (always use ARIA reflection lookup)- HTML global attributes with known property names (e.g.
tabindex→tabIndex)
fast build \
--templates="./components/**/*.html" \
--attribute-name-strategy=camelCase \
--entry=index.html \
--state=state.json \
--output=output.htmlConfiguration file
Instead of passing every option on the command line, you can place a fast-build.config.json file alongside your project files:
{
"entry": "index.html",
"state": "state.json",
"output": "output.html",
"templates": "./components/**/*.html",
"markers_v2": true
}The CLI automatically loads fast-build.config.json from the current directory when it exists. To use a different file, pass --config:
fast build --config=configs/my-build.jsonPrecedence: CLI arguments always override config file values. For example, --output=other.html will override the output value in the config file.
Path resolution: File paths in the config file (entry, state, output, templates) are resolved relative to the config file's directory, not the current working directory. This ensures the config works correctly regardless of where the CLI is invoked.
All keys are optional. Only the following keys are allowed: entry, state, output, templates, attribute-name-strategy, markers_v2, and stream. Unknown keys produce an error. Values must be strings except markers_v2 and stream, which must be JSON booleans (true or false). If state is omitted, rendering uses {}; if state is present, the referenced file must exist. CLI arguments always override config values, including --stream=false overriding "stream": true.
Convert
fast convert converts FAST declarative template files using the converter WASM module in wasm/convert/. Supported syntax names, output extensions, and default output suffixes are read from the converter WASM metadata so the Rust converter remains the source of truth for syntax targets.
fast convert --syntax=webui-prerelease --templates=example.html
fast convert --syntax=fast-v3-ts --templates=example.html --output=../*.template.ts
fast convert --syntax=fast-v3-ts --templates="src/**/*.html" --output=generated/*.template.ts
fast convert --syntax=fast-v3-ts --templates=example.html \
--type-source=MyElement --type-source-import=./my-element.jsConvert options
| Option | Default | Description |
|---|---|---|
--syntax="<syntax>" |
(required) | Target syntax: webui-prerelease, webui-framework-prerelease, or fast-v3-ts |
--templates="<glob>" |
(required) | Comma-separated glob pattern(s) matching .html templates to convert. An exact single file path is also accepted. Loads the converter WASM module once for the whole batch. |
--output="" |
Next to source | Output file path. webui-prerelease defaults to *.webui.html; webui-framework-prerelease defaults to *.webui-framework.html; fast-v3-ts defaults to *.template.ts. Any * in the output path is replaced with the input basename without extension. Every file matched by --templates is written under --output's directory (or next to its source if --output is omitted). |
--overwrite |
false |
Allow replacing an existing output file. CLI presence always means true. |
--typed[=true/false] |
false |
Only for fast-v3-ts. Add an explicit ElementViewTemplate<TSource> export annotation and type-only import. Explicit false overrides configured true. |
--type-source="<Name>" |
(none) | Only valid with --syntax=fast-v3-ts. Emits export const template = html<Name>\…`;instead of the untypedhtmlcall. Must be a dotted TypeScript identifier (e.g.MyElementorNamespace.MyElement`). |
--type-source-import="<module>" |
(none) | Requires --type-source. Also emits import type { <Name> } from "<module>"; alongside the helper imports. |
--config="" |
fast-convert.config.json |
Path to a JSON configuration file. If omitted, fast-convert.config.json in the current directory is used when present. CLI arguments take precedence over config values. |
Only matched .html files are converted; non-.html files and directories are never matched. The output extension must match the selected syntax: .html for webui-prerelease/webui-framework-prerelease and .ts for fast-v3-ts. The output parent directory is created automatically if it doesn't exist (mkdir -p semantics), and an existing output file is rejected unless --overwrite or "overwrite": true is used. When a --templates pattern matches zero files, a warning is printed but the command does not fail unless every pattern matches zero files.
webui-framework-prerelease converts FAST's declarative syntax into the Microsoft WebUI Framework's own declarative runtime syntax (webui-prerelease remains unchanged — it emits HTML for WebUI's separate FAST parser plugin instead). When webui-framework-prerelease strips an unsupported f-slotted/f-children directive, the CLI prints Warning: <message> to stderr for each stripped directive without failing the command.
--type-source exists so templates that combine more than one differently-namedref/children/slotted directive (which otherwise fail to type-check once a
real TSource is supplied, because html<TSource, TParent> infers a singleTSource for the whole tagged template) can be generated already type-checked
against a concrete element type. Seemicrosoft-fast-convert DESIGN.md.
Convert configuration file
fast-convert.config.json follows the same precedence and path-resolution rules as fast-build.config.json: CLI arguments override config values, and templates/output paths from config are resolved relative to the config file directory.
{
"syntax": "fast-v3-ts",
"templates": "src/**/*.html",
"output": "generated/*.template.ts",
"overwrite": false,
"type-source": "MyElement",
"type-source-import": "./my-element.js"
}Only syntax, templates, output, overwrite, type-source,type-source-import, and typed are allowed. Values must be strings exceptoverwrite and typed, which must be JSON booleans.
For declaration-emitting consumers, use --typed --type-source=MyElement.
The converter WASM's convert_template_with_options accepts an appended
optional typed boolean. convert_stylesheet(css, "fast-v3-ts", "styles", true) emits an ElementStyles annotation. Existing calls remain untyped;
typed exports are tested with TypeScript isolatedDeclarations.
Running converter fixtures locally
Build the package WASM, then run the fixture validation:
npm run build -w @microsoft/fast-build
npm run test:fixtures:convert -w @microsoft/fast-buildThe fixture tests run fast convert from fixture directories that containfast-convert.config.json, so they verify the same default config discovery used
by fast build. Generated fixture output is written underpackages/fast-build/test/.fixture-output/ and removed by the tests.test/fixtures/convert/type-source.test.js additionally type-checks--type-source output with the TypeScript compiler API against a companion
element type, using a separate test/.fixture-output-type-source/ directory.
Template syntax
Bindings and FAST directive markup inside HTML comments are unsupported.
Commenting out bound markup does not reliably disable its bindings and can cause
rendering errors or client hydration mismatches. Remove unused bound markup
instead, and keep explanatory comments free of binding syntax and FAST directive
tags.
Template syntax follows the FAST declarative HTML format. See the@microsoft/fast-element declarative documentation
for full documentation on bindings, conditionals, repeats, and directives.
Overview (`packages/fast-element/README.md`)
FAST Element
The fast-element library is a lightweight means to easily build performant, memory-efficient, standards-compliant Web Components. FAST Elements work in every major browser and can be used in combination with any front-end framework or even without a framework.
Installation
From NPM
To install the latest fast-element library using npm:
npm install --save @microsoft/fast-elementWithin your JavaScript or TypeScript code, you can then import library APIs like this:
import { FASTElement } from '@microsoft/fast-element';From CDN
A pre-bundled script that contains all APIs needed to build web components with FAST Element is available on CDN. You can use this script by adding type="module" to the script element and then importing from the CDN.
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module">
import { FASTElement } from "https://cdn.jsdelivr.net/npm/@microsoft/fast-element/dist/fast-element.min.js";
// your code here
</script>
</head>
</html>The markup above always references the latest release. When deploying to production, you will want to ship with a specific version. Here's an example of the markup for that:
<script type="module" src="https://cdn.jsdelivr.net/npm/@microsoft/[email protected]/dist/fast-element.min.js"></script>:::note
For simplicity, examples throughout the documentation will assume the library has been installed from NPM, but you can always replace the import location with the CDN URL.
:::
:::tip
Looking for a quick guide on building components? Check out our Cheat Sheet.
:::
Browser Requirements
FAST Element v3 assumes a client-side browser Window runtime with nativeglobalThis, Custom Elements, DOM, Shadow DOM, and requestAnimationFrame.
The FAST object is now a module-scoped export (not on globalThis). If you
need to support an older browser without globalThis, load that polyfill before
importing @microsoft/fast-element.
The FAST Element client runtime is not intended to execute in workers,
server-side JavaScript runtimes, or other non-window hosts. Use server-side
rendering tools to produce HTML for the browser, and run FAST Element in the
client window where custom elements connect, render, update, and hydrate.
Debug entrypoint
The declarative attribute fixture covers server-side omission of null/missing
attributes and subsequent hydration updates. Explicit empty strings remain
present, and omitted attributes retain binding slots. See the
server-rendering guide
for the rendering contract.
@microsoft/fast-element/debug.js exports enableDebug() instead of configuring
FAST at import time. The development root export and debug rollup bundle still
enable debug behavior automatically.
import { enableDebug } from "@microsoft/fast-element/debug.js";
enableDebug();Server-rendered hydration
Call enableHydration() from @microsoft/fast-element/hydration.js before
components connect to reuse their server-rendered DOM. Ordered sibling-bounded
views use pointer traversal and consume native markers without factory-index
arrays; unusual boundaries and potentially mutating cleanup retain the live
Range fallback. Nested and adjacent repeats pair only their own server ranges,
preserving SSR nodes while reconciling missing or excess items.
Call enableHydration() before components are registered with the platform to
also enable pointer-only native marker cleanup for FAST components that do not
observe data-fe. Observation tracking is opt-in and excluded from client-onlyFASTElement bundles. Earlier registrations still hydrate correctly through
the conservative fallback, as do unknown registration, observed markers,
customized built-ins, and overridden attribute methods. Cleanup remains
synchronous.
Existing FAST 3 marker output and opt-in markers_v2 remain supported without
application changes. See the server-rendering guide
and contributor performance workloads.
Contributor-facing internals separate DOM traversal, binding target mapping,
and repeat-range discovery; the opt-in benchmark spec is independent of the
deterministic correctness and allocation regression spec.
Export Sizes
Bundle sizes for each tree-shakeable export are tracked in SIZES.md and regenerated on every build. See the Export Sizes documentation page for the latest numbers.
Root Imports and Path Exports
The root @microsoft/fast-element entrypoint exports the FAST Element
implementation APIs, including the element base class, kernel, controller,
definition APIs, template APIs, binding helpers, directives, styles, and schema
helpers. The FAST element registry is also available from@microsoft/fast-element/registry.js for focused definition lookups andfastElementRegistry.whenRegistered(tagName) registration promises.
Declarative, hydration, context, and dependency injection APIs are available
from their focused path exports.
Focused package path exports remain available for consumers that want to import
a narrower entrypoint directly. The website's
Path Exports page is
generated from package.json and lists each supported path.
Dynamic Style Application
Import css, CSSTemplateTag, CSSValue, and style APIs such asElementStyles, CSSDirective, cssDirective, ComposableStyles,HostBehavior, HostController, StyleStrategy, and StyleTarget from@microsoft/fast-element:
import { ElementStyles, css } from "@microsoft/fast-element";When runtime state or external signals need to add or remove styles, create theElementStyles with css and toggle it throughthis.$fastController.addStyles() / this.$fastController.removeStyles() from
the element lifecycle or change handlers.
css templates remain static style definitions. Runtime CSS bindings and
behavior-producing CSS directives are no longer supported; keep the condition on
the element and toggle a separate ElementStyles instance through the
controller when styles need to change.
Declarative HTML
Bindings and FAST directive markup inside HTML comments are unsupported in
declarative templates. Commenting out bound markup does not reliably disable its
bindings; remove unused markup instead. See the
template syntax restrictions.
FAST Element publishes its declarative HTML runtime from@microsoft/fast-element/declarative.js. This entrypoint exports the
functional APIs for declarative templates: declarativeTemplate(),TemplateParser, and related configuration types. attributeMap() andobserverMap() are available from their own focused path exports. The
declarative runtime is pure at import time; declarative APIs lazily install only
declarative debug messages. Hydration is separate and remains opt-in throughenableHydration() from @microsoft/fast-element/hydration.js.
import { FASTElement } from "@microsoft/fast-element";
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
class MyElement extends FASTElement {}
MyElement.define({
name: "my-element",
template: declarativeTemplate(),
});declarativeTemplate() automatically defines FAST's internal native<f-template> publisher in the relevant registry, resolves the matching<f-template name="my-element">, and keeps the definition template concrete
before define() resolves. If multiple matching <f-template> elements are
connected, the first connected element supplies the template and later duplicates
do not reassign it. Consumers should not import or define the <f-template>
implementation directly.
Declarative schema behavior is enabled with define extensions:
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()],
);attributeMap() creates @attr-style accessors for leaf bindings, andobserverMap() creates deep observable accessors for discovered root
properties. Declarative templates assign definition.schema during template
resolution so these extensions always have schema data when used withdeclarativeTemplate(). For non-declarative/manual schema use, import Schema
from @microsoft/fast-element and pass it on the element definition;observerMap() can also receiveobserverMap({ schema }) directly. When both extensions are present, attribute
mapping runs before observer mapping.
Declarative utilities such as deepMerge are available from@microsoft/fast-element/declarative-utilities.js. Seedocs/declarative/syntax.md for declarative implementation
details and the
Declarative HTML docs
for guides on writing f-templates, defining declarative elements, and
server-side rendering. Declarative event bindings use $e for the DOM event
object and $c for the execution context.
Prerendered Content Optimization
Hydration of prerendered content is opt-in. Call enableHydration() from @microsoft/fast-element/hydration.js before any FAST elements connect to activate the hydration path:
import { enableHydration } from "@microsoft/fast-element/hydration.js";
const hydration = enableHydration();
await hydration.whenHydrated();
console.log("hydration complete");By default, hydration handles the initial prerendered batch and then no-ops
after the active hydration batch completes. If your app streams Declarative
Shadow DOM after the initial batch, keep the hydration hook active. In this
mode, enableHydration().whenHydrated() intentionally remains pending because
hydration never reaches a global completion point.
import { enableHydration, StopHydration } from "@microsoft/fast-element/hydration.js";
enableHydration({
stopHydration: StopHydration.never,
});FAST Element 3.x data-free hydration markers are the only format recognized
by default. Server output that still emits the legacy FAST Element 2.x
indexed markers will fail to hydrate unless you opt into that reader on its
dedicated export path — an interoperability enhancement for backend systems
that have not yet adopted the data-free format:
import { enableHydration, markers_v2 } from "@microsoft/fast-element/hydration.js";
enableHydration({ markers: markers_v2 });Because markers_v2 is only referenced when explicitly imported, its legacy
parsing logic is tree-shaken out of the bundle for consumers who don't use it.
When using @microsoft/fast-build, set "markers_v2": true infast-build.config.json so the renderer and client use the same format.
When hydration is enabled and a FAST element connects with an existing shadow root (from server-side rendering or declarative shadow DOM), ElementController detects this and hydrates instead of re-rendering. Two properties on the controller let you inspect the result:
isPrerendered: Promise— resolvestruewhen the element had a declarative shadow root (DSD) at connect time, regardless of whether hydration ran.isHydrated: Promise— resolvestrueonly when hydration actually ran successfully.
This enables several optimizations:
- Hydration instead of re-render: The template uses
hydrate()to map existing DOM nodes to binding targets rather than cloning new DOM. - Declarative template resolution:
declarativeTemplate()waits for the
matching<f-template>beforedefine()completes, so connected elements
hydrate with a concrete template. - Attribute skip:
onAttributeChangedCallback()skips processing during initial upgrade when the element is prerendered, since server-rendered attribute values are already correct. - Binding skip:
HTMLBindingDirective.bind()skipsupdateTargetforattributeandbooleanAttributeaspect types when the view is prerendered.
Hydration Mismatch Diagnostics
If a prerendered DOM diverges from the client template in a way FAST cannot
reconcile (the render() empty-boundary and repeat() count-mismatch cases
recover silently), HydrationBindingError or HydrationTargetElementError is
thrown. By default the message is a single line pointing at the opt-inhydrationDebugger(). Install the debugger to get a rich
"Expected … / Received …" report with the SSR HTML snippet and structuredexpected / received fields on the thrown error:
import { enableHydration, hydrationDebugger } from "@microsoft/fast-element/hydration.js";
enableHydration({ debugger: hydrationDebugger() });The debugger module is tree-shaken out of production hydration bundles when
not imported, so the rich diagnostic helpers only land in builds that opt in.
Component authors can await both promises to distinguish prerendered content from successful hydration:
const controller = this.$fastController;
const prerendered = await controller.isPrerendered;
const hydrated = await controller.isHydrated;
if (prerendered && !hydrated) {
// Had DSD but hydration wasn't enabled — client-side rendered
}Custom directives can also await controller.isPrerendered and controller.isHydrated (both Promise on the ViewController interface) to determine how the view's content was rendered.
Define Extensions
The static define() method on a FASTElement subclass accepts an optional second argument — an array of extension callbacks that are invoked with the resolved element definition before the element is registered with the platform. This enables a plugin pattern where reusable behaviors can hook into element registration.
import { FASTElement, type FASTElementExtension } from "@microsoft/fast-element";
function logger(): FASTElementExtension {
return definition => {
console.log(`Defining element: ${definition.name}`);
};
}
class MyComponent extends FASTElement {
// component code
}
MyComponent.define({ name: "my-component", template, styles }, [logger()]);Each extension receives the full FASTElementDefinition, which includes the resolved element name, type, template, styles, and attribute metadata. Extensions run before customElements.define(), so any setup they perform is available when existing DOM elements are upgraded.
Overview (`packages/fast-router/README.md`)
FAST Router
The @microsoft/fast-router package contains a history-based navigation and routing solution designed around Web Components. By using fast-router, you can create multi-page and full application experiences. The router works with any Web Components, but has special support for Web Components built on FAST.
Installation
From NPM
To install the fast-router library using npm:
npm install --save @microsoft/fast-routerWithin your JavaScript or TypeScript code, you can then import library APIs like this:
import { FASTRouter } from '@microsoft/fast-router';Looking for a setup that integrates with a particular front-end framework or bundler? Check out our integration docs.
Configuration at a Glance
The router is currently in alpha while we address a few bugs, finish up some final polish, and write the full documentation. Below is a quick sample of how to configure the router, to help get you going while we complete the full documentation.
A Sample Route Configuration
/* Detailed source-code truncated for AI context efficiency. */Using the Configuration with a Router
import { FASTElement, customElement } from '@microsoft/fast-element/fast-element.js';
import { css } from '@microsoft/fast-element/css.js';
import { html } from '@microsoft/fast-element/html.js';
import { FASTRouter } from '@microsoft/fast-router';
import { AppRouterConfiguration } from './routes';
FASTRouter;
const template = html<MainApplication>`
<fast-router :config=${x => x.routerConfiguration}></fast-router>
`;
const styles = css`
:host {
contain: content;
}
:host, fast-router {
display: block;
width: 100%;
height: 100%;
}
`;
@customElement({
name: 'main-application',
template,
styles
})
export class MainApplication extends FASTElement {
routerConfiguration = new AppRouterConfiguration();
}Overview (`packages/fast-test-harness/README.md`)
FAST Test Harness
The fast-test-harness package is a Playwright testing harness for FAST Element web components with CSR and SSR support.
Requirements
- Node.js 22.18 or later
- Playwright 1.56 or later
Installation
To install fast-test-harness using npm:
npm install --save-dev @microsoft/fast-test-harnessTest directory setup
The harness serves a Vite dev server from a test/ directory in your project. CSR and SSR modes use different entry points from the same directory.
test/
├── index.html # CSR: loads main.ts
├── ssr.html # SSR: template with comment placeholders
├── vite.config.ts # Vite config (shared by both modes)
└── src/
├── main.ts # CSR: registers components, applies theme
├── entry-client.ts # SSR: registers components for hydration
└── entry-server.ts # SSR: exports render() for fixture generationWriting tests
Import test and expect from the harness. Configure the component tag name with test.use(), then call fastPage.setTemplate() in each test to render it.
import { expect, test } from "@microsoft/fast-test-harness";
test.describe("Button", () => {
test.use({ tagName: "my-button", innerHTML: "Click me" });
test("should render", async ({ fastPage }) => {
await fastPage.setTemplate();
await expect(fastPage.element).toBeVisible();
});
test("should accept attributes", async ({ fastPage }) => {
await fastPage.setTemplate({
attributes: { appearance: "primary", disabled: true },
});
await expect(fastPage.element).toHaveAttribute("appearance", "primary");
});
test("should work inside a form", async ({ fastPage }) => {
await fastPage.setTemplate(`
<form>
<my-button type="submit">Submit</my-button>
</form>
`);
await expect(fastPage.element).toBeVisible();
});
});Use updateTemplate() to modify attributes or innerHTML after the initial render without navigating away from the page:
await fastPage.setTemplate();
await fastPage.updateTemplate(fastPage.element, { attributes: { disabled: true } });The toHaveCustomState assertion checks ElementInternals custom states:
await expect(element).toHaveCustomState("checked");Fixture options
| Option | Type | Default | Description |
|---|---|---|---|
base |
string |
"/" |
Base path for the test page and SSR endpoint |
tagName |
string |
"" |
Custom element tag name |
innerHTML |
string |
"" |
Default inner HTML |
waitFor |
string[] |
[] |
Additional elements to wait for before testing |
ssr |
boolean |
false |
Use SSR mode (or set PLAYWRIGHT_TEST_SSR=true) |
CSR files
index.html loads a script that registers your components:
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8" /></head>
<body>
<script type="module" src="/src/main.ts"></script>
</body>
</html>main.ts registers components and applies global config. The body starts empty; setTemplate() injects HTML per test.
import "./define-all.js";
import { setTheme } from "./theme.js";
setTheme(lightTheme);SSR files
ssr.html contains comment placeholders the server fills in per request:
<!doctype html>
<html lang="en">
<head>
<title></title>
</head>
<body>
<script type="module" src="/src/entry-client.ts"></script>
</body>
</html>entry-client.ts imports the harness SSR entry (which enables hydration) and registers components. Component definition modules should use template: declarativeTemplate(); this automatically defines FAST's internal <f-template> publisher.
import "@microsoft/fast-test-harness/ssr/entry-client.js";
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
import { MyButton, definition } from "../../src/button/index.js";
MyButton.define({
name: definition.name,
template: declarativeTemplate(),
});entry-server.ts exports a render() function that the server calls for each setTemplate() request. It returns three strings that get injected into ssr.html:
export function render(queryObj: Record<string, string>): {
template: string; // <f-template> HTML →
fixture: string; // rendered element HTML →
preloadLinks: string; // <link> tags →
};Use createSSRRenderer from the harness to build the render() function. It scans for component build artifacts (f-templates, stylesheets) and uses the @microsoft/fast-build WASM renderer to produce declarative shadow DOM on each request.
Multi-component package (all components in one package):
import { createSSRRenderer } from "@microsoft/fast-test-harness/ssr/render.js";
const { render } = createSSRRenderer({
packageName: "@my-scope/web-components",
tagPrefix: "my",
});
export { render };Per-component packages (each component is a separate npm package):
import { createSSRRenderer } from "@microsoft/fast-test-harness/ssr/render.js";
const { render } = createSSRRenderer({
tagPrefix: "my",
components: [
{ name: "button", packageName: "@my-scope/button" },
{ name: "checkbox", packageName: "@my-scope/checkbox" },
],
});
export { render };Server
The package includes a Node.js HTTP server with Vite middleware that handles both CSR page serving and SSR fixture generation. Run it directly or let Playwright manage it via webServer:
// playwright.config.ts
export default defineConfig({
webServer: {
command: "fast-test-harness",
port: 3278,
reuseExistingServer: true,
},
});For custom setup, import startServer:
import { startServer } from "@microsoft/fast-test-harness/server.mjs";
await startServer(process.cwd(), "./test", "./test/vite.config.ts", {
port: 4000,
debug: true,
});One server can host several test directories at separate paths. Set the matchingbase fixture option for each test group.
await startServer(process.cwd(), process.cwd(), "./vite.config.ts", {
routes: [
{ base: "/button/", root: "./packages/button/test" },
{ base: "/dialog/", root: "./packages/dialog/test" },
],
});
test.use({ base: "/button/", tagName: "my-button" });| Parameter | Default | Description |
|---|---|---|
cwd |
process.cwd() |
Static file serving root |
root |
<cwd>/test |
Vite root (contains index.html, ssr.html) |
configFile |
Vite auto-discovery | Vite config path |
options.port |
3278 |
Server port |
options.base |
/ |
Base URL path |
options.debug |
false |
Write SSR fixtures to temp/ for inspection |
options.routes |
Single route from root and base |
Test roots keyed by URL base path |
CLI flags
fast-test-harness [command] [options]
Commands:
serve Start the test harness dev server (default)
generate-templates Generate <f-template> HTML files from compiled templates
generate-stylesheets Generate CSS files from compiled ElementStyles
generate-webui-templates Generate WebUI-compatible DSD templates
Serve options:
-p, --port <number> Server port (default: 3278)
-b, --base <path> Base URL path (default: /)
-r, --root <path> Vite root directory (default: <cwd>/test)
-c, --config <path> Vite config file path (default: Vite auto-discovery)
-d, --debug Write SSR fixtures to temp/ for inspection
-v, --version Show version number
-h, --help Show help messageCLI flags take precedence over environment variables.
generate-templates preserves browser-valid <template> wrappers and strips
static wrappers even when the opening or closing tag includes ASCII whitespace
before >.
| Environment variable | Default | Description |
|---|---|---|
PORT |
3278 |
Server port (overridden by --port) |
BASE |
/ |
Base URL path (overridden by --base) |
FAST_DEBUG |
— | Set "true" to enable debug mode (overridden by --debug) |
PLAYWRIGHT_TEST_SSR |
— | Set "true" for SSR mode |
SSR renderer
createSSRRenderer(options) scans for component build artifacts and returns a { render } object compatible with the server's entry-server.ts contract. It uses the @microsoft/fast-build WASM module to parse f-templates and render them into declarative shadow DOM, including wrapper tags whose opening or closing > is preceded by ASCII whitespace.
| Option | Type | Description |
|---|---|---|
tagPrefix |
string |
Tag name prefix for custom elements (e.g., "fluent", "contoso") |
packageName |
string? |
Monolithic package name — scans subdirectories for component artifacts. Mutually exclusive with components. |
components |
ComponentRegistration[]? |
Explicit list of per-component packages. Mutually exclusive with packageName. |
distDir |
string? |
Artifact directory relative to the package root (default: "dist/esm"). Only used with packageName. |
themeStylesheet |
string? |
URL or package specifier for a global theme stylesheet. |
Exports
| Specifier | Contents |
|---|---|
@microsoft/fast-test-harness |
test, expect, CSRFixture, SSRFixture, toHaveCustomState, installDomShim, createSSRRenderer |
@microsoft/fast-test-harness/build/*.js |
installDomShim, generateStylesheets, generateFTemplates, generateWebuiTemplates, definitionAsyncResolver, shadowOptionsToAttributes, ShadowOptionsResolver |
@microsoft/fast-test-harness/fixtures/*.js |
CSRFixture, SSRFixture, toHaveCustomState, extended test and expect |
@microsoft/fast-test-harness/ssr/render.js |
createSSRRenderer, renderTemplate, buildEntryHtml, buildState |
@microsoft/fast-test-harness/ssr/entry-client.js |
Enables FAST Element hydration for SSR pages |
@microsoft/fast-test-harness/server.mjs |
startServer |
@microsoft/fast-test-harness/playwright.config.mjs |
Shared Playwright configuration |
@microsoft/fast-test-harness/vite.config.mjs |
Shared Vite configuration |
@microsoft/fast-test-harness/public/* |
Static assets (base CSS) |
Overview (`sites/benchmarks/README.md`)
Benchmarking FAST templates and rendering scenarios
This package benchmarks FAST Element and FAST HTML rendering scenarios. It uses Playwright to automate Chrome, collects CDP trace data, computes timing statistics, and generates an interactive HTML report.
Prerequisites
- Node.js
>=22.12.0 - npm
>=10 - Dependencies installed from repo root:
npm installRunning benchmarks
From this folder (sites/benchmarks):
npm run benchThis will:
- Build the scenarios with Vite (into
server/dist) - Start the static server on
http://localhost:5174 - Run all discovered scenarios through Playwright
- Print stats tables to the console
- Generate an interactive HTML report at
results/latest.html
Iteration count
Default is 50 iterations per benchmark. Override with BENCH_ITERATIONS:
BENCH_ITERATIONS=100 npm run benchRunning specific scenarios
Pass scenario names as arguments to filter which benchmarks run:
npm run bench -- basic attr-reflectEach argument is matched as a substring, so basic runs basic/csr and basic/hydration. You can also target a specific variant:
npm run bench -- basic/csrRunning from repo root
npm run bench -w @microsoft/fast-benchProject structure
src/
bench.ts Runner orchestrator (Playwright + CDP)
chart.ts SVG chart rendering
report.ts HTML report assembly
trace.ts CDP trace event parsing + stats
scenarios/
harness.ts Shared benchmark runtime
tree.ts Tree data generator for scenarios
report.css Report styles (inlined into output)
report.js Report interaction (inlined into output)
index.html Dev server landing page
basic/ Scenario directories...
attr-reflect/
bind-event/
dot-syntax/
ref-slotted/
repeat/
when/Each scenario follows a consistent structure:
<scenario>/
element.ts Shared element class definition
template.html <f-template> for hydration variants
csr/
index.html Client-side rendering entry
main.ts CSR variant script
hydration/
index.html SSR + hydration entry (uses )
main.ts Hydration variant script
render.ts SSR HTML generator for Vite build-time renderingOutput
The report at results/latest.html (symlinked to the most recent timestamped run) includes:
- Overlaid line charts per scenario with all variants (csr, hydration) on shared axes
- Hover-to-emphasize and click-to-select variant interaction
- Winner table showing the best value per metric/stat, color-coded by variant
- Variant drill-down — click a variant to see its full stats
Metrics collected per iteration:
| Metric | Description |
|---|---|
| Scripting | JS execution (EvaluateScript, FunctionCall, etc.) |
| Layout | Layout computation |
| Style Recalc | Style recalculation |
| Paint | Paint, compositing, layerization |
| Total (trace) | Sum of the above four |
| User Measure | Duration of the bench performance.measure |
| FCP | First Contentful Paint (relative to navigation start) |
| LCP | Largest Contentful Paint (relative to navigation start) |
| DCL | DOMContentLoaded (relative to navigation start) |
Summary statistics: Min, Median, Mean, Geometric Mean, P95, Max. All values are in milliseconds.
Dev server
For iterating on scenarios without running the full benchmark:
npm run serveThis starts Vite on http://localhost:5173 with hot reload.
Overview (`sites/website/README.md`)
Website
This website is built using 11ty, a modern static website generator.
Installation
Run the following commands in the root directory:
npm ci
npm run buildLocal Development
npm startBuild
npm run buildThis command generates static content into the build directory and can be served using any static content hosting service.
Version Banners
The site supports configurable banners on documentation pages to communicate version status (e.g., stable, legacy, prerelease). Banners are defined in src/_data/versionBanners.js and rendered via src/_includes/version-banner.njk.
Configuration
Edit src/_data/versionBanners.js to add, remove, or modify banners. Each version key maps to a configuration object:
"3.x": {
enabled: false, // Toggle the banner on/off
type: "stable", // "legacy" | "stable" | "prerelease"
message: "You are viewing the current stable version of FAST.",
}| Type | Color | Use case |
|---|---|---|
legacy |
Gray | Previous versions — links to latest docs |
stable |
Green | Current stable release |
prerelease |
Amber | Upcoming / release candidate versions |
Prerelease banners should include any version-specific copy directly in the message.
Styles
Banner CSS lives in src/css/version-banner.css and is loaded on all pages via root.njk.
Overview (`crates/microsoft-fast-build/benchmark-baselines/README.md`)
Renderer performance baselines
Run all commands from the repository root. These are explicit measurements,
not portable timing assertions or automatically refreshed golden files.
npm run build -w @microsoft/fast-build
npm run bench:render -w @microsoft/fast-build -- --case=url_sink_siblings --output=/tmp/fast-render.json
npm run bench:render -w @microsoft/fast-build -- --case=url_sink_siblings --check-scaling
cargo bench --manifest-path crates/microsoft-fast-build/Cargo.toml --bench renderThe native Criterion groups separate rendering with parsed state, state parsing
plus rendering, and locator construction. The Node runner calls the packaged
WASM export in a fresh --expose-gc child for each sample, warms it once with up
to 1,000 instances (configurable with --warmup-count), forces
GC, and reports median/p95 wall time, CPU time, JSON stringify time, input/output
bytes, throughput, before/after memory, process peak RSS, and parent-sampled RSS
(100 ms on macOS/Linux). Peak RSS includes warmup and verification, not just the
measured call. No in-child timer is used to observe a blocked WASM call.
Sampled RSS is null if the child exits before a sample or the platform is not
macOS/Linux. Process peak RSS remains available independently of timer sampling.
Node throughput uses the requested rendered-instance count (not only custom
host count); Criterion reports instances/second for render phases and template
definitions/second for locator construction.
--case, --counts, and --samples narrow a run. Both marker modes always run.
Omit --case to run the full matrix; repeat/state-clone workloads can be very
expensive, so begin with the primary URL regression on a development machine.
Definitions use 50/200/1,000 definitions with payloads 1/20; other cases use
1,000/5,000/20,000 instances. State overlays run with 0/200 extra root keys, with
and without host attributes. corpus.json is the shared recipe used by native
benchmarks, Rust parity tests, and the Node fixture generator.
Use --compare= to enforce output hash/length parity and report
speedups. --check-scaling explicitly checks the URL case's 20k/5k ratio <= 5
and fits setup + coefficient * count^exponent from all three sizes, requiring
exponent <= 1.15. Timing checks are opt-in: do not enable them on unpinned PR
runners. At least three samples on otherwise idle, stable hardware are recommended.
Output paths are created exclusively; a run never overwrites an existing baseline.
Reports snapshot the starting git revision, dirty-worktree flag, and WASM
SHA-256. A dirty worktree can include benchmark helpers, reports, or generated
files; it does not necessarily mean the renderer implementation is uncommitted.
Use the artifact hash and source provenance together.
Local before/after measurements
The checked-in aarch64-apple-darwin-before.json andaarch64-apple-darwin-after.json measure the URL sibling regression on an Apple
M3 Pro with Node 22.22.1, three fresh-child samples per size/mode, and a
1,000-instance warmup. These are local macOS measurements, not Linux/reference
machine results or full-corpus performance claims.
| Marker mode | Instances | Before median (ms) | After median (ms) | Speedup |
|---|---|---|---|---|
| v3 | 1,000 | 299.682 | 7.999 | 37.47x |
| v3 | 5,000 | 7,091.213 | 32.416 | 218.75x |
| v3 | 20,000 | 116,906.540 | 122.098 | 957.48x |
| v2 | 1,000 | 287.428 | 8.046 | 35.72x |
| v2 | 5,000 | 7,037.736 | 33.219 | 211.86x |
| v2 | 20,000 | 115,477.367 | 122.144 | 945.42x |
All six before/after output hashes and byte lengths match. The optimized
20k/5k ratios are 3.77 (v3) and 3.68 (v2), with fitted exponents 0.99 and
0.96 respectively. Both pass the 1.15 exponent threshold. The original v3
ratio is 16.49 with exponent 2.02; its--check-scaling run intentionally failed after writing the baseline report.
Process peak RSS at 20,000 instances was 106.06 -> 108.94 MiB (v3) and
99.17 -> 114.52 MiB (v2). This scanner change does not demonstrate a memory
reduction. Peak RSS includes warmup and hash verification; shorter optimized
calls also make 100 ms RSS samples sparse or unavailable.
The before artifact was built from a38c2f1aea2840ea530de42d055f0a03cefe5872
before renderer edits, with the new benchmark helpers present. Its WASM SHA-256 isc749f46306b561bb55e45700cf19f7ed3db8ad67d01cd8e9a3b7198943c4ca51.
The after artifact was built from 3e9117770a8da29c369d3d31a04e37da25fa08fb;
its WASM SHA-256 isc128e5d88ff457dedc0549438ec9e2d11450e0a89e997899b003898e587063bc.
Both reports retain their starting provenance even if the worktree changes
during measurement.
To reproduce an optimized comparison after building the package:
node packages/fast-build/test/performance/render-entry.mjs --case=url_sink_siblings --compare=crates/microsoft-fast-build/benchmark-baselines/aarch64-apple-darwin-before.json --output=/tmp/fast-render-after.json --check-scalingProfiling the remaining workloads
The aarch64-apple-darwin-*-profile.json reports were captured after the scanner
fix at a58548a5cec9f4508fd46693f9ec63443e07b0b6. Five reports contain 36
fresh-child packaged-WASM measurements in both marker modes; the native report
summarizes six symbolized CPU captures and separate Criterion quick estimates.
No renderer optimization was made during this investigation.
The WASM runs use three samples and a 250-instance warmup. Repeat and plain
controls use 250/1,000 instances, state cases use 1,000/5,000 hosts with 0/200
extra root keys, and definitions use 50/200/1,000 templates with payloads 1/20.
These bounded sizes avoid multiplying an already expensive state-cloning path
into an unhelpful full-matrix run. They do not establish 20k acceptance targets.
| Packaged-WASM workload | Instances | Extra root keys | v3 median (ms) | v2 median (ms) |
|---|---|---|---|---|
| Repeated custom elements | 1,000 | 0 | 422.71 | 431.95 |
| Bound plain elements | 1,000 | 0 | 15.06 | 14.81 |
| Attribute overlays | 1,000 | 0 | 4.30 | 4.37 |
| Attribute overlays | 1,000 | 200 | 35.25 | 35.27 |
| No attribute overlays | 1,000 | 200 | 3.70 | 3.84 |
| Many definitions, payload 20 | 1,000 | 0 | 43.68 | 46.72 |
Findings and next optimization
Child-state construction is the strongest measured next target. In native v3
profiles, build_custom_element_child_root occurs in 1,075/2,001 main-thread
samples (53.72%) for repeated custom elements and 1,205/2,049 (58.81%) for
attribute overlays with 200 root keys. Its descendant stacks includeRawTable::clone, Vec::clone, String::clone, and allocator/copy routines.
Separate destruction stacks and frequent free routines show additional cleanup
work outside those inclusive counts.
The repeat fixture's root contains the entire items array. Every attributed
child clones that root before overlaying host attributes; increasing the
number of items therefore increases both the number and size of those copies.
Packaged-WASM repeat times rise from 53.55 to 422.71 ms (v3) and 45.86 to
431.95 ms (v2) for a 4x instance increase. This is evidence for the remaining
state-copying hazard, not a fitted 1k/5k/20k scaling claim. Removing attributes
is not a valid optimization because it would change state semantics.
The overlay controls reinforce this finding: adding 200 unused root keys
increases v3 time from 4.30 to 35.25 ms at 1,000 attributed hosts, while the
no-overlay case changes only from 3.40 to 3.70 ms. Output hashes within each
overlay/no-overlay workload are unchanged by those extra unused keys.
Borrowed child-state overlays should be the next focused optimization, with
state precedence/inheritance parity preserved.
The many-definition case has a different profile. Native locator construction
takes about 0.243 ms for 1,000 definitions with payload 20, versus about
34.48 ms for rendering with an already prepared locator. Code-sample
preprocessing appears in 1,257/2,148 (58.52%) locator-phase samples, but that
phase is small here; its percentage must not be interpreted as 58.52% of
end-to-end rendering. The render profile instead contains string searching,
attribute parsing/binding resolution, escaping, and copying. Compiled
templates, parse-once attributes, and buffered sinks remain relevant follow-ons.
Native quick estimates are independent, low-sample diagnostic runs; do not
subtract them to infer isolated parsing costs. Native sampling percentages
are inclusive stack frequencies, not exclusive CPU time, allocation counts,
or WASM phase percentages. Zero observed frames do not prove zero work,
especially with inlining. Native allocator behavior does not establish WASM
memory behavior. Raw captures remain local; the compact report retains their
hashes, selected symbols/counts, compiler/build flags, and binary provenance.
Reproducing the investigation
Use new output paths; these commands do not refresh the checked-in reports:
npm run bench:render -w @microsoft/fast-build -- --case=repeat_custom_elements --counts=250,1000 --samples=3 --warmup-count=250 --output=/tmp/fast-repeat-profile.json
npm run bench:render -w @microsoft/fast-build -- --case=plain_bound_elements --counts=250,1000 --samples=3 --warmup-count=250 --output=/tmp/fast-plain-profile.json
npm run bench:render -w @microsoft/fast-build -- --case=large_state_overlay --counts=1000,5000 --samples=3 --warmup-count=250 --output=/tmp/fast-overlay-profile.json
npm run bench:render -w @microsoft/fast-build -- --case=large_state_no_overlay --counts=1000,5000 --samples=3 --warmup-count=250 --output=/tmp/fast-no-overlay-profile.json
npm run bench:render -w @microsoft/fast-build -- --case=many_definitions --samples=3 --warmup-count=250 --output=/tmp/fast-definitions-profile.json
CARGO_PROFILE_BENCH_DEBUG=true RUSTFLAGS="-C force-frame-pointers=yes" cargo bench --manifest-path crates/microsoft-fast-build/Cargo.toml --bench render -- '^repeat_custom_elements/v3/render/1000/payload1/root0$' --profile-time 8 --noplotFor a native CPU capture, run the final command in one terminal. Once Criterion
prints Profiling for, attach macOS sample 3 1 -file <new-path>
from another terminal. Sample the benchmark executable, not Cargo. Building
first with the same environment and --no-run --message-format=json reveals
its path in the compiler-artifact record. A direct invocation of that binary
must include --bench, otherwise Criterion performs a correctness run rather
than a profiling loop. The profiling environment is opt-in and does not change
production WASM build settings or add dependencies.
The six filters used for CPU captures were:
^repeat_custom_elements/v3/render/1000/payload1/root0$
^plain_bound_elements/v3/render/1000/payload1/root0$
^large_state_overlay/v3/render/1000/payload1/root200$
^large_state_no_overlay/v3/render/1000/payload1/root200$
^many_definitions/v3/locator/1000/payload20/root0$
^many_definitions/v3/render/1000/payload20/root0$Replace --profile-time 8 with --quick for diagnostic timing estimates.
The compact native report sums counts across call-graph entries for each
named non-recursive function; recursive frame totals must not be summed as if
they represented distinct samples. Platform-specific Linux profiling remains
outstanding; native allocation baselines are described below.
Native allocation baselines
aarch64-apple-darwin-allocations.json records 30 isolated release-build samples
using allocation-counter 0.8.1. Each request starts a fresh native process;
the feature-gated example and its integration test are the only executables
that use this development allocator. No library allocator, WASM export,
production build setting, or rendering behavior changes.
Run from the repository root, writing only to a new output path:
node crates/microsoft-fast-build/benches/profile-allocations.mjs --output=/tmp/fast-allocations.json
cargo run --manifest-path crates/microsoft-fast-build/Cargo.toml --release --features allocation-profiling --example profile-allocations -- --case=large_state_overlay --count=1000 --root-keys=200 --mode=v2 --phase=render --output=/tmp/fast-overlay-allocations.json
cargo test --manifest-path crates/microsoft-fast-build/Cargo.toml --features allocation-profiling --test allocation-profiling
node --test crates/microsoft-fast-build/benches/profile-allocations.test.mjsSingle-request options are --case, --count, --payload, --root-keys,--mode=v3|v2, --phase=render|parse_state_and_render|locator, and --output,
all in --option=value form. Defaults are repeat/250/payload 1/root 0/v3/render.
Counts/payloads must be positive; only definition workloads accept payload
changes, and only large-state workloads accept root-key changes. Unknown,
duplicate, or invalid options fail explicitly. Reports refuse overwrites,
including a second exclusive-create check at write time.
Runner matrix/provenance/output-safety tests also run throughnpm run test:node -w @microsoft/fast-build; they do not invoke Cargo.
The feature-enabled Rust integration test above exercises the actual counter.
The matrix covers repeat/plain controls at 100/250/1,000 instances, overlays
with/without attributes and 0/200 extra root keys at 1,000 hosts, state parsing
plus rendering at 1,000 repeat/plain items, and all three phases for 1,000
definitions with payload 20. Every case runs in both marker modes. One sample
per request is recorded; these counters are not timing medians.
Measurement boundaries and interpretation
Fixture generation, pre-parsed state, and prepared locators are outside the
measurement. render counts only rendering; parse_state_and_render also
counts parsing the state JSON; locator counts cloning/preprocessing templates
and building the new locator, not template-JSON parsing or rendering.
Locator output is validated by rendering afterward, outside the counter.
Output hashes and byte lengths are checked against ordinary native rendering
in default tests, without asserting platform-specific allocation thresholds.
The returned HTML string or locator stays alive at the counter boundary:retainedBytes includes its capacity, not just logical HTML length, and does
not indicate a leak. peakLiveBytes excludes inputs allocated before the
phase; it is not total process heap, RSS, or WASM memory pages. Byte and block
peaks are independent maxima. Cumulative totalBytes and totalBlocks count
allocation events, including replacement allocations during reallocation,
not the number or size of currently live objects. This profiler captures the
current thread only; future threaded rendering would require another strategy.
The counter uses allocate/copy/free reallocation rather than in-place system
reallocation: event totals and temporary old/new-buffer overlap reflect that
instrumentation strategy, not necessarily the production allocator's peaks.
It does not collect allocation-site stacks or measure bytes copied/allocator
overhead, and should not be used for uninstrumented speed comparisons.
Provenance includes starting commit/dirty status, release executable SHA-256,
Cargo.lock SHA-256, compiler, target, and allocator identification. The matrix
rejects changed provenance or duplicate samples. The checked-in captures were
made with the new harness uncommitted against renderer source8b9c524f3a6a7544919588e708c4ad0c98a8197b; their binary/lock hashes identify
the measured development artifact. Neither captured timing nor allocation
data is silently regenerated by tests.
Findings
| Native v3 render workload | Instances | Extra root keys | Allocation events | Cumulative MiB | Peak newly-live MiB |
|---|---|---|---|---|---|
| Repeated custom elements | 250 | 0 | 1,016,297 | 83.61 | 1.11 |
| Repeated custom elements | 1,000 | 0 | 15,315,047 | 1,265.75 | 4.45 |
| Bound plain elements | 1,000 | 0 | 147,047 | 11.02 | 2.73 |
| Attribute overlays | 1,000 | 0 | 62,913 | 3.02 | 0.48 |
| Attribute overlays | 1,000 | 200 | 1,462,913 | 122.14 | 0.48 |
| No attribute overlays | 1,000 | 200 | 41,012 | 1.54 | 0.28 |
| Definitions, payload 20 | 1,000 | 0 | 893,012 | 63.03 | 4.21 |
The 4x repeat increase produces about 15x more allocation events/bytes, while
plain controls remain approximately proportional to instance count. Adding
200 unused root keys multiplies attributed-host allocation volume by about
40x, yet leaves the no-attribute control unchanged. Within each state control,
unused keys do not change output hashes. Both marker modes show the same
state-copying pattern; native locator counters are identical across modes
because marker rendering is outside that phase.
This strengthens the case for borrowed child-state overlays as the next
runtime optimization. It is a baseline, not the proposed 50% allocation
reduction or any 20k/stable-host/Linux/WASM acceptance claim.
Output compatibility and remaining work
Correctness runs in normal Rust/Node tests at 0/1/2/1,000 instances in both marker
modes. The shared benches/fixtures/expected-baseline.json records pre-optimization
SHA-256, byte length, host count, binding/repeat marker counts, and attribute marker
counts. It also verifies normal/legacy-stream byte parity. Regeneration is explicit:
node packages/fast-build/test/performance/render-entry.mjs --golden=/tmp/fast-golden.jsonReview changes against the checked-in golden; do not replace it just to make a
regression pass. Profiles and output hashes must be compared with the same
toolchain and fixture recipe. The runner cannot isolate internal WASM phases,
WASM memory pages (not exported), native allocation counts, or file writes; those
require separate profiling. The schema does not invent measurements for them.
Rust parity tests also render every remaining-workload WASM report record and
verify its output hash, bytes, and marker counts, including the larger
definition payloads and state-overlay controls. They do not assert timing.
The completed optimization addresses directive discovery only. Compiled templates, buffered
recursive writes, state overlays, prepared WASM handles, and genuinely incremental
sessions remain separate work. stream: true still materializes all chunks.
Overview (`examples/csr/todo-app/README.md`)
Todo app tutorial
A To-Do app, written entirely with fast-element.
Note: A step by step tutorial will be coming to the site soon. In the mean
time, you can runnpm installand thennpm startto see the example.
Note: This example uses an LSP, to enable this in VS Code try the following:
- Open a javascript/typescript file and use the command palette to
Select typescript versionand choose the local workspace version.
Design system
This example consumes the shared @microsoft/fast-examples-design-system
workspace package. The design-system package is CSS-only — there are no
JavaScript or TypeScript exports. src/main.ts imports@microsoft/fast-examples-design-system/tokens.css once, and the rest of the
app references the registered --fast-* design tokens through var(...).
The app uses the light theme. <html data-theme="light"> is set
statically in index.html; the app does not implement a
theme toggle. (The shared design-system package supports dark and
system-preference themes via the same data-theme attribute — seeexamples/design-system/README.md — but this
example deliberately keeps the surface area minimal and demonstrates only
token consumption.)
For the shared package overview, the token catalog, and authoring rules,
see examples/design-system/README.md andexamples/design-system/DESIGN.md.
Token usage
The todo app styles use shared tokens for layout, surfaces, typography, and
interaction states. For example:
- App shell surface:
--fast-background-layer-primary-solid,--fast-foreground-ctrl-neutral-primary-rest,--fast-stroke-divider-subtle,--fast-corner-large,--fast-shadow-card-rest,--fast-padding-content-medium. - Primary action (Add):
--fast-background-ctrl-brand-rest,--fast-foreground-ctrl-on-brand-rest,--fast-stroke-divider-brand,--fast-background-ctrl-brand-hover,--fast-background-ctrl-brand-pressed. - Typography:
--fast-text-style-default-regular-font-family,--fast-text-global-body3-font-sizepaired with--fast-text-global-body3-line-height,--fast-text-global-display2-font-sizepaired with--fast-text-global-display2-line-height. - Destructive action (per-row delete button):
--fast-status-danger-tint-foreground(red text at rest),--fast-status-danger-tint-background(subtle red hover fill),--fast-status-danger-tint-stroke(focus outline). - Focus rings:
--fast-ctrl-focus-outer-strokepaired with--fast-ctrl-focus-outer-stroke-width. - Motion:
--fast-duration-fastand--fast-curve-easy-easefor hover and
press transitions.
No design value (color, font size, line height, padding, gap, corner,
stroke width, shadow, duration, or curve) is hard-coded in the app's
component styles.
Overview (`examples/csr/todo-mobx-app/README.md`)
MobX Todo App (FAST)
A small Todo app that uses MobX as its state library and @microsoft/fast-element for its Web Components. It is the MobX counterpart to examples/csr/todo-app, and the two render an identical UI — the same <h1>FAST Todos</h1>, <todo-form> (input + "Add Todo"), <select> filter (All / Active / Completed), and `` of rows with a checkbox, description, and × button. Like every example app it consumes the shared @microsoft/fast-examples-design-system tokens for styling.
The example shows that you do not need a custom decorator or any bridge code to integrate MobX with @microsoft/fast-element — a single autorun per component, set up in connectedCallback and disposed in disconnectedCallback, is all that is needed.
Running
# from the monorepo root
npm ci
npm run build -w @microsoft/fast-element
npm start -w @microsoft/fast-todo-mobx-app-exampleThe Vite dev server runs at http://localhost:9001.
To produce a production build:
npm run build -w @microsoft/fast-todo-mobx-app-exampleThe output is written to examples/csr/todo-mobx-app/www/.
The pattern, in one component
<todo-app> mirrors the slices of MobX state it renders into FAST @observable fields with a single autorun set up in connectedCallback. The template binds to those plain FAST observables as usual.
import { FASTElement, observable } from "@microsoft/fast-element";
import { autorun } from "mobx";
import { type Todo, type TodoListFilter, todoStore } from "./state/index.js";
export class TodoApp extends FASTElement {
@observable public items: readonly Todo[] = [];
@observable public activeFilter: TodoListFilter = "all";
private _disposer?: () => void;
public connectedCallback(): void {
this._disposer = autorun(() => {
this.items = todoStore.filtered.map(t => ({
id: t.id,
description: t.description,
done: t.done,
}));
this.activeFilter = todoStore.activeFilter;
});
super.connectedCallback();
}
public disconnectedCallback(): void {
super.disconnectedCallback();
this._disposer?.();
this._disposer = undefined;
}
public activeFilterChanged(
_prev: TodoListFilter | undefined,
next: TodoListFilter | undefined,
): void {
if (next !== undefined && next !== todoStore.activeFilter) {
todoStore.setFilter(next);
}
}
}That is the entire integration. The template binds ${x => x.items}, ${x => x.activeFilter}, etc. exactly as it would for any other FAST element.
Why this works
autorunruns immediately, then re-runs whenever any MobX-observable it read changes.- Each re-run reassigns FAST
@observablefields, which fires FAST's own change notification, which patches the bound DOM. - The
.map(...)copies each todo into a plain{id, description, done}object — that simultaneously creates a fresh array reference on every run (so FAST always sees the assignment) and ensures the autorun depends on every observable field it serializes. - Calling
autorunbeforesuper.connectedCallback()ensures the mirror fields have correct values before the template binds, avoiding any initial flicker. - The disposer returned by
autorunis stored on the element and released on disconnect, so reactions never outlive the element.
The <select> element uses a twoWay binding to activeFilter. activeFilterChanged propagates user changes back to the MobX store (gated by an equality check, so the autorun's store-driven assignment never loops).
What the app demonstrates
| Feature | Where to look |
|---|---|
makeAutoObservable store with observables, computeds, and actions inferred automatically |
src/state/todo-store.ts |
autorun persisting to localStorage and hydrating on load |
src/state/persistence.ts |
autorun bridging MobX → FAST in a component |
src/todo-app.ts |
UI-local FAST @observable for the input draft (deliberately not in the MobX store) |
src/todo-form.ts |
| Filtered list view backed by a MobX computed | src/state/todo-store.ts (get filtered) |
Two-way binding from a <select> back into the MobX store |
src/todo-app.template.ts, src/todo-app.ts (activeFilterChanged) |
| Styling exclusively via the shared design-system tokens | src/*.styles.ts, index.html, src/main.ts |
Project layout
examples/csr/todo-mobx-app/
├── index.html # mounts <todo-app>, sets data-theme="light"
├── package.json
├── tsconfig.json
├── vite.config.ts
├── README.md # this file
├── DESIGN.md # why the autorun pattern, lifecycle details
└── src/
├── design-system.d.ts # module shim for the tokens stylesheet import
├── exports.ts # barrel for tooling
├── main.ts # entry — imports tokens.css, wires storage, defines <todo-app>
├── state/ # MobX layer (framework-agnostic)
│ ├── index.ts
│ ├── persistence.ts
│ └── todo-store.ts
├── todo-app.{ts,template.ts,styles.ts}
└── todo-form.{ts,template.ts,styles.ts}See DESIGN.md for an explanation of why this pattern is sufficient.
License
MIT — see the monorepo root.
Overview (`examples/ssr/chat-app/README.md`)
Chat App Example
A small declarative FAST chat demo that pre-renders its initial markup with @microsoft/fast-build and streams canned assistant replies with a hidden iframe plus document.write().
Design system
This example consumes the shared @microsoft/fast-examples-design-system
workspace package. The design-system package is CSS-only — src/main.ts
imports @microsoft/fast-examples-design-system/tokens.css once, andpublic/styles.css references the registered --fast-* tokens throughvar(...).
The app uses the dark theme statically. <html data-theme="dark"> is set
in entry.html, so the FAST build step carries that attribute
into the generated index.html. The example does not implement a runtime
theme toggle.
For the shared package overview, the token catalog, and authoring rules, see../../design-system/README.md and../../design-system/DESIGN.md.
What it demonstrates
- declarative FAST custom elements via subclass
define()calls +@microsoft/fast-element/declarative.js<f-template>s - initial markup rendered from
entry.html+templates.html - chunked assistant response streaming with the Jake Archibald iframe technique
- mixed native HTML and FAST custom elements in each canned reply
- exact-match canned prompts with a fallback response
Suggested prompts
Use these exact prompts to walk through the conversation:
HiHow are you?What's the weather like?Tell me a jokeWhat can you do?Goodbye
Run locally
From the repository root:
npm run build -w @microsoft/fast-chat-app-example
npm run start -w @microsoft/fast-chat-app-exampleOr from this folder:
npm run build
npm startThe build first regenerates index.html from entry.html, state.json, and templates.html, then runs TypeScript and Vite.
Files
entry.html— source document for the FAST build stepstate.json— initial state used while pre-rendering the page shelltemplates.html— declarative FAST templates for the custom elementsbuild-markup.mjs— runs@microsoft/fast-buildand injects the templates intoindex.htmlpublic/styles.css— token-based shared stylesheet for the page shell and component shadow rootssrc/— runtime classes, canned conversation data, and hydration bootstrapindex.html— generated pre-rendered page used by Vite
See DESIGN.md for architecture details, design-system notes, and the canned conversation data.
Overview (`examples/ssr/webui-todo-app/README.md`)
WebUI Todo App
A Todo app demonstrating FAST declarative HTML templates with
webui prerendering and hydration.
This example shows how to combine @microsoft/fast-element/declarative.js declarative templates
with @microsoft/webui for server-side prerendering, producing a fast initial
render that hydrates into a fully interactive FAST web component application.
Features
- Declarative templates: Uses
.htmland.cssfiles instead of
imperativehtml/csstagged template literals - Server-side prerendering:
webui servecompiles declarative templates
and renders initial state into static HTML - Client-side hydration:
@microsoft/fast-element/declarative.jspicks up prerendered DOM
and attaches FAST reactive bindings - Todo functionality: Add items via a form, filter by status (all,
active, completed), toggle completion via native checkbox, delete items
Prerequisites
From the monorepo root, install dependencies and build packages:
npm ci
npm run buildRunning
# Build client bundle and start the webui dev server
npm start -w @microsoft/fast-webui-todo-app-exampleThe app will be available at http://localhost:8081.
For development with live reloading, run the client and server in separate
terminals:
# Terminal 1: Watch and rebuild client bundle
npm run start:client -w @microsoft/fast-webui-todo-app-example
# Terminal 2: Start webui dev server with --watch
npm run start:server -w @microsoft/fast-webui-todo-app-exampleDesign system
This example consumes the shared @microsoft/fast-examples-design-system
workspace package. The design-system package is CSS-only — there are no
JavaScript or TypeScript exports. src/index.ts imports@microsoft/fast-examples-design-system/tokens.css once, and the rest of the
app references the registered --fast-* design tokens through var(...).
The app uses the light theme. <html data-theme="light"> is set
statically in src/index.html; the app does not implement
a runtime theme toggle. (The shared design-system package supports dark and
system-preference themes via the same data-theme attribute — see../../design-system/README.md — but this
example deliberately keeps the SSR surface area minimal and demonstrates only
token consumption.)
For the shared package overview, the token catalog, and authoring rules, see../../design-system/README.md and../../design-system/DESIGN.md.
Architecture
Server-side (`webui serve`)
The webui CLI serves the app with the --plugin=fast flag, which enables
FAST-aware template processing:
- Scans
./srcfor declarative HTML templates (.htmlfiles wrapped in<f-template>or containing<template shadowrootmode="open">) - Reads initial state from
./data/state.json - Renders templates server-side with
{{}}bindings resolved from state - Injects Declarative Shadow DOM for instant first paint
- Serves the bundled client assets from
./dist
src/index.ts imports @microsoft/fast-examples-design-system/tokens.css.
When esbuild bundles the entry point, it emits both dist/index.js anddist/index.css. src/index.html links /index.css so the shared --fast-*
tokens are available during prerendering and after hydration.
Templates (`src/todo-app/`, `src/todo-item/`)
Each component uses co-located files:
*.html— Declarative template with<template shadowrootmode="open">
and FAST bindings*.css— Scoped styles loaded via<link rel="stylesheet">in the shadow
DOM*.ts— Component class usingFASTElementwithdefine()
Client entry (`src/index.ts`)
Bootstraps hydration by:
- Enabling hydration with
enableHydration({ markers: markers_v2 })— themarkers_v2reader is required because thewebui --plugin=fastserver
still emits FAST Element 2.x indexed hydration markers - Importing components, which register themselves via subclass
define()calls
usingdeclarativeTemplate()andobserverMap() - Resolving matching
<f-template>definitions to hydrate prerendered shadow DOM
Binding syntax
| Syntax | Scope | Example |
|---|---|---|
{{expr}} |
Server + client | {{title}} — HTML-escaped value |
{{{expr}}} |
Server + client | Raw unescaped HTML |
{expr} |
Client-only | {onClick(e)} — event handlers, refs |
@event="{handler}" |
Client-only | @submit="{onSubmit()}", @change="{onToggleChange()}" |
?attr="{{expr}}" |
Server + client | ?disabled="{{!description}}", ?checked="{{state == 'done'}}" |
f-ref="{prop}" |
Client-only | f-ref="{addInput}" — element reference |
$e |
Event arg | @input="{onInput($e)}" — current event |
Template directives
| Directive | Purpose | Example |
|---|---|---|
<for each="item in items"> |
Iteration | <for each="item in items"><todo-item>...</todo-item></for> |
| `` | Conditional | ... |
Packages Fast Element Docs Architecture Intro (`packages/fast-element/docs/architecture/intro.md`)
Introduction
This document (and the linked documents) explains how the exports and side effects of @microsoft/fast-element are used to create custom elements.
@microsoft/fast-element is designed to run in a client-side browser Window
runtime. Its element lifecycle, rendering, and update queue assume browser DOM,
Custom Elements, Shadow DOM, and requestAnimationFrame are available. Workers,
server-side JavaScript runtimes, and other non-window hosts are not supported
targets for the FAST Element client runtime.
Glossary
- Overview: How the
@microsoft/fast-elementshould be used by a developer and what code is executed during the first render. FASTElement: How theFASTElementis architected.htmltagged template literal: How thehtmltagged template literal takes and converts the contents into aViewTemplate.Updatesqueue: How updates to attributes and observables are processed.
Packages Fast Element Docs Architecture Overview (`packages/fast-element/docs/architecture/overview.md`)
General FAST usage
FASTElement is an extension of HTMLElement which makes use of Custom Element APIs native to the browser. It also supplies the following methods:
- The
definemethod combines the Custom Element name, template, style, and other options into aFASTElementDefinition, then uses the native Custom Elementdefine()to register the Custom Element with a Custom Element Registry. - The
frommethod allows the use of Customized Built-in elements, which extend from native elements such asHTMLButtonElement.
Creating a Custom Element from FASTElement
A basic developer flow for defining a custom element looks like this:
flowchart TD
A[Create a <code>FASTElement</code> web component by extending the <code>FASTElement</code> class] --> F
F[Prepare the definition with template and styles] --> G[Define the component in the browser with <code>define()</code>]Let's take a look at the definition step to see what the FAST architecture is doing at this stage.
Defining a Custom Element
The subclass define() method creates a new FASTElementDefinition, which includes all metadata needed for the element (such as templates, attributes, and styles), and registers it with the configured Custom Element Registry. The resulting object can be retrieved via ElementController.definition.
A Custom Element in JavaScript is sent to the Browser
Let's step back from defining the Custom Element and consider what is happening when we import from the @microsoft/fast-element package.
The FAST Element client runtime is expected to run in the browser Window.
Custom element definition, connection, rendering, and update scheduling all rely
on browser APIs such as the Custom Element Registry, DOM, Shadow DOM, andrequestAnimationFrame.
First, a global FAST property will be created if one does not already exist, typically in browser on the window.
Additionally, when Custom Elements are included in a script a few things might happen even before a Custom Element gets detected by the browser. First, there are initial side effects caused by the use of decorators. These include the attr decorator from @microsoft/fast-element and the observable decorator from @microsoft/fast-element.
Here is a basic flow of what code is executed and when during initial load of a script that contains a FAST defined Custom Element:
flowchart TD
A@{ shape: circle, label: "The browser loads the JavaScript file containing FAST and FAST Custom Element definitions" }
B@{ shape: rect, label: "The FAST global is added to the window" }
A --> B
C@{ shape: rect, label: "A Custom Element executes the <code>compose</code> step"}
B --> C
D@{ shape: procs, label: "<ul style='text-align: left'><li>Any defined observable decorators are added to the FAST global</li><li>An attribute decorator locates the associated Custom Elements constructor which it uses to push itself to the Custom Elements attribute collection</li></ul>"}
C --> D
F@{ shape: rect, label: "An HTML tagged template literal is executed for the FAST Custom Element and applied to the definition" }
D --> F
G@{ shape: rect, label: "The <code>define</code> step is hit and the <code>customElement</code> is registered with the Custom Element Registry" }
F --> G
H@{ shape: rect, label: "A Custom Element is detected on the page and the browser initializes it" }
I@{ shape: dbl-circ, label: "The lifecycle steps for <code>FASTElement</code> are executed" }
G --> H
H --> ISites Website Src Docs 2.X Getting Started (`sites/website/src/docs/2.x/getting-started.md`)
layout: 2x eleventyNavigation: key: getting-started2x title: Getting Started order: 2 navigationOptions: activeKey: getting-started2x permalink: false
Sites Website Src Docs 3.X Getting Started (`sites/website/src/docs/3.x/getting-started.md`)
layout: 3x eleventyNavigation: key: getting-started3x title: Getting Started order: 2 navigationOptions: activeKey: getting-started3x permalink: false
Sites Website Src Docs 1.X Api Fast Colors.Histogram.Gethistogramindex (`sites/website/src/docs/1.x/api/fast-colors.histogram.gethistogramindex.md`)
id: fast-colors.histogram.gethistogramindex title: Histogram.getHistogramIndex property hide_title: true layout: 1x
@microsoft/fast-colors > Histogram > getHistogramIndex
Histogram.getHistogramIndex property
Signature:
getHistogramIndex: (r: number, g: number, b: number) => number;Sites Website Src Docs 1.X Api Fast Colors.Matchlightnessindex (`sites/website/src/docs/1.x/api/fast-colors.matchlightnessindex.md`)
id: fast-colors.matchlightnessindex title: matchLightnessIndex() function hide_title: true layout: 1x
@microsoft/fast-colors > matchLightnessIndex
matchLightnessIndex() function
Takes the input color and compares it to each color in the reference array to find the index with the closest Lightness value in HSL color space
Signature:
export declare function matchLightnessIndex(input: ColorRGBA64, reference: ColorRGBA64[]): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| input | ColorRGBA64 | |
| reference | ColorRGBA64[] |
Returns:
number
Sites Website Src Docs 1.X Api Fast Element.Executioncontext.Index (`sites/website/src/docs/1.x/api/fast-element.executioncontext.index.md`)
id: fast-element.executioncontext.index title: ExecutionContext.index property hide_title: true layout: 1x
@microsoft/fast-element > ExecutionContext > index
ExecutionContext.index property
The index of the current item within a repeat context.
Signature:
index: number;Sites Website Src Docs 1.X Api Fast Element.Htmldirective.Targetindex (`sites/website/src/docs/1.x/api/fast-element.htmldirective.targetindex.md`)
id: fast-element.htmldirective.targetindex title: HTMLDirective.targetIndex property hide_title: true layout: 1x
@microsoft/fast-element > HTMLDirective > targetIndex
HTMLDirective.targetIndex property
The index of the DOM node to which the created behavior will apply.
Signature:
targetIndex: number;Sites Website Src Docs 1.X Api Fast Element.Nodebehaviorfactory.Targetindex (`sites/website/src/docs/1.x/api/fast-element.nodebehaviorfactory.targetindex.md`)
id: fast-element.nodebehaviorfactory.targetindex title: NodeBehaviorFactory.targetIndex property hide_title: true layout: 1x
@microsoft/fast-element > NodeBehaviorFactory > targetIndex
NodeBehaviorFactory.targetIndex property
The index of the DOM node to which the created behavior will apply.
Signature:
targetIndex: number;Sites Website Src Docs 1.X Api Fast Element.Splice.Index (`sites/website/src/docs/1.x/api/fast-element.splice.index.md`)
id: fast-element.splice.index title: Splice.index property hide_title: true layout: 1x
@microsoft/fast-element > Splice > index
Splice.index property
The index that the splice occurs at.
Signature:
index: number;Sites Website Src Docs 1.X Api Fast Foundation.Datagrid.Focuscolumnindex (`sites/website/src/docs/1.x/api/fast-foundation.datagrid.focuscolumnindex.md`)
id: fast-foundation.datagrid.focuscolumnindex title: DataGrid.focusColumnIndex property hide_title: true layout: 1x
@microsoft/fast-foundation > DataGrid > focusColumnIndex
DataGrid.focusColumnIndex property
The index of the column that will receive focus the next time the grid is focused. This value changes as focus moves to different rows within the grid. Changing this value when focus is already within the grid moves focus to the specified column.
Signature:
focusColumnIndex: number;Sites Website Src Docs 1.X Api Fast Foundation.Datagrid.Focusrowindex (`sites/website/src/docs/1.x/api/fast-foundation.datagrid.focusrowindex.md`)
id: fast-foundation.datagrid.focusrowindex title: DataGrid.focusRowIndex property hide_title: true layout: 1x
@microsoft/fast-foundation > DataGrid > focusRowIndex
DataGrid.focusRowIndex property
The index of the row that will receive focus the next time the grid is focused. This value changes as focus moves to different rows within the grid. Changing this value when focus is already within the grid moves focus to the specified row.
Signature:
focusRowIndex: number;Sites Website Src Docs 1.X Api Fast Foundation.Datagridrow.Rowindex (`sites/website/src/docs/1.x/api/fast-foundation.datagridrow.rowindex.md`)
id: fast-foundation.datagridrow.rowindex title: DataGridRow.rowIndex property hide_title: true layout: 1x
@microsoft/fast-foundation > DataGridRow > rowIndex
DataGridRow.rowIndex property
The index of the row in the parent grid. This is typically set programmatically by the parent grid.
Signature:
rowIndex: number;Sites Website Src Docs 1.X Api Fast Foundation.Listbox.Selectedindex (`sites/website/src/docs/1.x/api/fast-foundation.listbox.selectedindex.md`)
id: fast-foundation.listbox.selectedindex title: Listbox.selectedIndex property hide_title: true layout: 1x
@microsoft/fast-foundation > Listbox > selectedIndex
Listbox.selectedIndex property
The index of the selected option.
Signature:
selectedIndex: number;Sites Website Src Docs 1.X Api Index (`sites/website/src/docs/1.x/api/index.md`)
id: index title: API Reference hide_title: true layout: 1x
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-animation | |
| @microsoft/fast-colors | |
| @microsoft/fast-components | |
| @microsoft/fast-element | |
| @microsoft/fast-foundation |
Sites Website Src Docs 1.X Components Getting Started (`sites/website/src/docs/1.x/components/getting-started.md`)
id: components-quickstart
title: Components QuickStart
eleventyNavigation:
key: components-quickstart1x
parent: components1x
title: Components QuickStart
navigationOptions:
activeKey: components-quickstart1x
keywords:
- getting started
layout: 1x
The @microsoft/fast-components library contains Web Components built on top of our standard component and design system foundation. @microsoft/fast-components expresses the FAST design language (FAST Frame).
Installation
From NPM
To install the components:
npm install --save @microsoft/fast-componentsyarn add @microsoft/fast-componentsTo use a Web Component as a custom element in HTML, the custom element must be registered. Importing the necessary component definition into your JavaScript bundle and registering it with a DesignSystem will register the custom element with the platform. Here's how to do that for @microsoft/fast-components:
import {
fastButton,
provideFASTDesignSystem
} from "@microsoft/fast-components";
provideFASTDesignSystem()
.register(
fastButton()
);As a shortcut, if you wish to easily register all available components, rather than registering each one, you can use the following pattern:
import {
allComponents,
provideFASTDesignSystem
} from "@microsoft/fast-components";
provideFASTDesignSystem().register(allComponents);:::note
When working with a tree-shaking supporting bundler such as Webpack or Rollup, you will want to import and register the individual components. This will ensure that any unused components are tree-shaken out of your builds.
:::
:::tip
Looking for a setup that integrates with a particular front-end framework or bundler? Check out our integration docs.
:::
From CDN
A pre-bundled script that contains all APIs needed to use the components is available on CDN. You can use this script by adding type="module" to the script element and then importing from the CDN. Here's what that would look like for the FAST Web Components:
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module" src="https://cdn.jsdelivr.net/npm/@microsoft/fast-components/dist/fast-components.min.js"></script>
</head>
</html>The markup above always references the latest release of the components. When deploying to production, you will want to ship with a specific version. Here's an example of the markup for that:
<script type="module" src="https://cdn.jsdelivr.net/npm/@microsoft/[email protected]/dist/fast-components.min.js"></script>When using FAST from CDN, there is no need to register the components. The CDN script includes code that automatically sets up the design system and registers all the components.
:::note
For simplicity, examples throughout the documentation will assume the library has been installed from NPM, but you can always replace the import location with the CDN URL.
:::
Add components
With the components registered, add any component to the HTML. That's it! For a comprehensive list of all elements, see the Components section.
<fast-button>Hello world</fast-button>Sites Website Src Docs 1.X Design Systems Overview (`sites/website/src/docs/1.x/design-systems/overview.md`)
id: overview
title: What is a Design System?
eleventyNavigation:
key: overview1x
parent: design-systems1x
title: What is a Design System?
navigationOptions:
activeKey: overview1x
keywords:
- design systems
layout: 1x
A Design System can be generally thought of as a collection of interactive media resources that promotes brand alignment. While that definition is intentionally broad, in UI development, Design Systems manifest as component libraries surrounded by usage guidance and design principles.
How does FAST facilitate development of design systems?
FAST helps by both providing a highly configurable design system that you can drop into any app and by providing a set of building blocks you can use to construct your own design system from scratch, with very little work.
A highly configurable design system
FAST Frame is the name of our highly configurable design system. It's made up of a set of web components and accompanying design tokens. The visual design can be impacted in myriad ways using the exported Design Tokens, element templates and styles can be augmented or overridden, and shadow roots can be opened or closed as necessary for your application. If you’re looking to get up and running with FAST Web Components and the FAST Frame design system, start here.
Design Tokens
Design Tokens are powerful tools that are used to express design abstractions like color, typography, spacing units, and other design-led values. FAST exposes powerful tools for creating and using Design Tokens for your Design System. FAST Frame leverages FAST's design tokens extensively. FAST Frame's use of design tokens is so extensive that you might be able to create your own design system simply by modifying the existing tokens. Of course, you can always create your own tokens and even base your tokens on compositions of existing tokens. To learn more see our documentation on design tokens.
Your own design system based on FAST components
If you need more control than you can get through modifying design token values or recipes, you can wrap your styling around the foundation components. By exporting raw JavaScript Custom Element classes, @microsoft/fast-foundation handles the business-logic driving a large catalog of UI components. These Custom Element classes can be taken as-is and composed with your templates and styles to match your application or library requirements, reducing the development time and overhead of creating your own design system. For example, rather than implementing a Tree View from scratch, you can use the Foundation Tree View class and simply compose it with your own CSS to create a reusable web component as part of your own system.
Sites Website Src Docs 1.X Fast Element Getting Started (`sites/website/src/docs/1.x/fast-element/getting-started.md`)
id: getting-started
title: Getting Started with FAST Element
eleventyNavigation:
key: getting-started1x
parent: fast-element1x
title: Getting Started with FAST Element
navigationOptions:
activeKey: getting-started1x
keywords:
- getting started
layout: 1x
The fast-element library is a lightweight means to easily build performant, memory-efficient, standards-compliant Web Components. FAST Elements work in every major browser and can be used in combination with any front-end framework or even without a framework.
Installation
From NPM
To install the fast-element library, use either npm or yarn as follows:
npm install --save @microsoft/fast-elementyarn add @microsoft/fast-elementWithin your JavaScript or TypeScript code, you can then import library APIs like this:
import { FASTElement } from '@microsoft/fast-element';:::tip
Looking for a setup that integrates with a particular front-end framework or bundler? Check out our integration docs.
:::
From CDN
A pre-bundled script that contains all APIs needed to build web components with FAST Element is available on CDN. You can use this script by adding type="module" to the script element and then importing from the CDN.
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module">
import { FASTElement } from "https://cdn.jsdelivr.net/npm/@microsoft/fast-element/dist/fast-element.min.js";
// your code here
</script>
</head>
</html>The markup above always references the latest release. When deploying to production, you will want to ship with a specific version. Here's an example of the markup for that:
<script type="module" src="https://cdn.jsdelivr.net/npm/@microsoft/[email protected]/dist/fast-element.min.js"></script>:::note
For simplicity, examples throughout the documentation will assume the library has been installed from NPM, but you can always replace the import location with the CDN URL.
:::
:::tip
Looking for a quick guide on building components? Check out our Cheat Sheet.
:::
Sites Website Src Docs 2.X Api Fast Element.Executioncontext.Index (`sites/website/src/docs/2.x/api/fast-element.executioncontext.index.md`)
id: "fast-element.executioncontext.index" title: "ExecutionContext.index property" layout: 2x-api eleventyNavigation: key: "api2xfast-element.executioncontext.index" parent: "api2xfast-element" title: "ExecutionContext.index property" navigationOptions: activeKey: "api2xfast-element.executioncontext.index"
@microsoft/fast-element > ExecutionContext > index
ExecutionContext.index property
The index of the current item within a repeat context.
Signature:
index: number;Sites Website Src Docs 2.X Api Fast Element.Splice.Index (`sites/website/src/docs/2.x/api/fast-element.splice.index.md`)
id: "fast-element.splice.index" title: "Splice.index property" layout: 2x-api eleventyNavigation: key: "api2xfast-element.splice.index" parent: "api2xfast-element" title: "Splice.index property" navigationOptions: activeKey: "api2xfast-element.splice.index"
@microsoft/fast-element > Splice > index
Splice.index property
Signature:
index: number;Sites Website Src Docs 2.X Api Index (`sites/website/src/docs/2.x/api/index.md`)
id: "index" title: "API Reference" layout: 2x eleventyNavigation: key: "api2xapi2x" parent: "api2x" title: "@microsoft/fast-element" navigationOptions: activeKey: "api2xapi2x"
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-element |
Sites Website Src Docs 3.X Api Fast Element.Defaultexecutioncontext.Index (`sites/website/src/docs/3.x/api/fast-element.defaultexecutioncontext.index.md`)
id: "fast-element.defaultexecutioncontext.index" title: "DefaultExecutionContext.index property" layout: 3x-api eleventyNavigation: key: "api3xfast-element.defaultexecutioncontext.index" parent: "api3xfast-element" title: "DefaultExecutionContext.index property" navigationOptions: activeKey: "api3xfast-element.defaultexecutioncontext.index"
@microsoft/fast-element > DefaultExecutionContext > index
DefaultExecutionContext.index property
The index of the current item within a repeat context.
Signature:
index: number;Sites Website Src Docs 3.X Api Fast Element.Executioncontext.Index (`sites/website/src/docs/3.x/api/fast-element.executioncontext.index.md`)
id: "fast-element.executioncontext.index" title: "ExecutionContext.index property" layout: 3x-api eleventyNavigation: key: "api3xfast-element.executioncontext.index" parent: "api3xfast-element" title: "ExecutionContext.index property" navigationOptions: activeKey: "api3xfast-element.executioncontext.index"
@microsoft/fast-element > ExecutionContext > index
ExecutionContext.index property
The index of the current item within a repeat context.
Signature:
index: number;Sites Website Src Docs 3.X Api Fast Element.Splice.Index (`sites/website/src/docs/3.x/api/fast-element.splice.index.md`)
id: "fast-element.splice.index" title: "Splice.index property" layout: 3x-api eleventyNavigation: key: "api3xfast-element.splice.index" parent: "api3xfast-element" title: "Splice.index property" navigationOptions: activeKey: "api3xfast-element.splice.index"
@microsoft/fast-element > Splice > index
Splice.index property
Signature:
index: number;Sites Website Src Docs 3.X Api Index (`sites/website/src/docs/3.x/api/index.md`)
id: "index" title: "API Reference" layout: 3x eleventyNavigation: key: "api3xapi3x" parent: "api3x" title: "@microsoft/fast-element" navigationOptions: activeKey: "api3xapi3x"
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-element | Core APIs for building standards-based Web Components with FAST Element. |
Sites Website Src Docs 3.X Declarative Templates Overview (`sites/website/src/docs/3.x/declarative-templates/overview.md`)
id: declarative-templates-overview
title: Declarative HTML Overview
layout: 3x
eleventyNavigation:
key: declarative-templates-overview3x
parent: declarative-templates3x
title: Overview
navigationOptions:
activeKey: declarative-templates-overview3x
keywords:
- declarative
- html
- templates
- web components
- f-template
{% raw %}
Declarative HTML Overview
FAST Element provides two ways to author web component templates:
- Imperative — JavaScript tagged template literals using the
htmlfunction. - Declarative — Plain HTML
<f-template>elements parsed at runtime.
The declarative model moves template authoring from JavaScript into HTML. Because templates are expressed as standard markup, they can be rendered by any server-side technology without running the FAST Element client runtime — enabling true stack-agnostic server-side rendering. Hydration and client rendering still run in the browser Window.
When to Use Declarative Templates
Choose declarative HTML when:
- You want server-rendered initial HTML that hydrates on the client.
- Your rendering pipeline does not run JavaScript (e.g. Rust, Go, or .NET servers).
- You prefer authoring templates in HTML files rather than JavaScript modules.
- You need the same template to be consumable by both a build tool and the browser.
The imperative html tagged template remains the right choice for purely client-side applications where templates are authored alongside component logic in TypeScript.
Hello World
A declarative FAST component requires three pieces: a JavaScript class, an <f-template> element, and a host element in the page.
1. Define the component class (main.ts):
import { attr, FASTElement } from "@microsoft/fast-element";
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
class HelloWorld extends FASTElement {
@attr greeting: string = "Hello";
}
HelloWorld.define({
name: "hello-world",
template: declarativeTemplate(),
});2. Write the template (templates.html):
<f-template name="hello-world">
<template>
<p>{{greeting}}, world!</p>
</template>
</f-template>3. Use the element in your page (index.html):
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"></head>
<body>
<hello-world greeting="Hi"></hello-world>
<script type="module" src="./main.ts"></script>
</body>
</html>When the page loads, declarativeTemplate() automatically defines FAST's internal <f-template> publisher in the relevant registry, finds the <f-template name="hello-world"> element, parses its inner <template>, and resolves the template for the hello-world definition. If you call enableHydration() before elements connect, matching pre-rendered content is hydrated; otherwise the component renders client-side. Reactive bindings keep the DOM in sync with property changes.
How It Works
The declarative pipeline follows these steps:
- Define — The component class is registered with
template: declarativeTemplate(), which tells FAST to wait for a matching<f-template>element and automatically defines the<f-template>custom element in the registry. - Parse — When
<f-template>connects to the DOM,TemplateParserconverts the declarative markup into the same internalViewTemplatethat the imperativehtmltag produces. - Resolve — The template is resolved through the bridge and assigned to the element definition before
define()completes. - Render or hydrate — If
enableHydration()was called and the page contains pre-rendered content, existing DOM nodes are reused. Otherwise the template renders client-side.
Declarative vs. Imperative Syntax
The same component can be expressed either way. Here is a side-by-side comparison:
Imperative:
import { attr, FASTElement, html } from "@microsoft/fast-element";
const template = html<NameTag>`
<h3>${x => x.greeting}</h3>
`;
class NameTag extends FASTElement {
@attr greeting: string = "Hello";
}
NameTag.define({ name: "name-tag", template });Declarative:
import { attr, FASTElement } from "@microsoft/fast-element";
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
class NameTag extends FASTElement {
@attr greeting: string = "Hello";
}
NameTag.define({
name: "name-tag",
template: declarativeTemplate(),
});<f-template name="name-tag">
<template>
<h3>{{greeting}}</h3>
</template>
</f-template>Both produce the same runtime behavior. The declarative version separates the template into HTML, making it available for server-side rendering without JavaScript.
Next Steps
- Writing f-templates — Template syntax, bindings, directives, and expressions.
- Defining Elements — Component registration and extension configuration.
- Server-Side Rendering — Rendering templates on the server and hydrating on the client.
{% endraw %}
Sites Website Src Docs 2.X Api Fast Element Context Index (`sites/website/src/docs/2.x/api/fast-element/context/index.md`)
id: "index" title: "API Reference" layout: 2x eleventyNavigation: key: "api2x@microsoft/fast-element/context.js2x" parent: "api2x" title: "@microsoft/fast-element/context.js" navigationOptions: activeKey: "api2x@microsoft/fast-element/context.js2x"
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-element/context.js |
Sites Website Src Docs 2.X Api Fast Element Di Index (`sites/website/src/docs/2.x/api/fast-element/di/index.md`)
id: "index" title: "API Reference" layout: 2x eleventyNavigation: key: "api2x@microsoft/fast-element/di.js2x" parent: "api2x" title: "@microsoft/fast-element/di.js" navigationOptions: activeKey: "api2x@microsoft/fast-element/di.js2x"
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-element/di.js |
Sites Website Src Docs 3.X Api Fast Element Context Index (`sites/website/src/docs/3.x/api/fast-element/context/index.md`)
id: "index" title: "API Reference" layout: 3x eleventyNavigation: key: "api3x@microsoft/fast-element/context.js3x" parent: "api3x" title: "@microsoft/fast-element/context.js" navigationOptions: activeKey: "api3x@microsoft/fast-element/context.js3x"
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-element/context.js |
Sites Website Src Docs 3.X Api Fast Element Declarative Index (`sites/website/src/docs/3.x/api/fast-element/declarative/index.md`)
id: "index" title: "API Reference" layout: 3x eleventyNavigation: key: "api3x@microsoft/fast-element/declarative.js3x" parent: "api3x" title: "@microsoft/fast-element/declarative.js" navigationOptions: activeKey: "api3x@microsoft/fast-element/declarative.js3x"
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-element/declarative.js |
Sites Website Src Docs 3.X Api Fast Element Di Index (`sites/website/src/docs/3.x/api/fast-element/di/index.md`)
id: "index" title: "API Reference" layout: 3x eleventyNavigation: key: "api3x@microsoft/fast-element/di.js3x" parent: "api3x" title: "@microsoft/fast-element/di.js" navigationOptions: activeKey: "api3x@microsoft/fast-element/di.js3x"
API Reference
Packages
| Package | Description |
|---|---|
| @microsoft/fast-element/di.js |
Packages Fast Element Docs ACKNOWLEDGEMENTS (`packages/fast-element/docs/ACKNOWLEDGEMENTS.md`)
Acknowledgements
There are many great open source projects that have inspired us and enabled us to build FAST. Below are a few that stand out.
- .NET - Many thanks to the .NET team for sharing their policies and practices upon which our issue management and release planning docs and practices were based.
- asap - An awesome, time-tested async task queue implementation that heavily inspired our DOM update system.
- Aurelia 1 and Aurelia 2 - Various details of Aurelia 2's decorator and metadata model inspired us in the design of
fast-element. Additionally, Aurelia 1's mechanisms for array observation, binding, and template compilation also guided us in our work. - faastjs - A project with a similar name but a very different purpose. Their API documentation approach leveraging api-extractor was a huge help to us.
- Knockout - One of the first JavaScript libraries (if not the first) to implement an observer system. The original techniques for observables and computed observables have influenced many libraries over the years. Re-interpreting these ideas in terms of modern JavaScript and DOM has helped us to build a powerful and robust system.
- lit-html - One of the first libraries to leverage standard JavaScript tagged template literals for HTML templates. We were inspired by this technique and wanted to explore whether it could be combined with our idea of arrow function binding expressions.
- Polymer - One of the first libraries (if not the first) to embrace Web Components.
- Vue - We liked the terseness of the
:and@syntax in templates, so we adapted it along with some modifications in our templates.
Packages Fast Element Docs Api Report.Api (`packages/fast-element/docs/api-report.api.md`)
API Report File for "@microsoft/fast-element"
Do not edit this file. It is a report generated by API Extractor.
/* Detailed source-code truncated for AI context efficiency. */Packages Fast Element Docs Template Bindings (`packages/fast-element/docs/template-bindings.md`)
FAST Element Template Bindings Architecture
This document explains how the FAST Element html tagged template system stores bindings, applies them to DOM elements, triggers updates on data changes, and handles HTML-specific behaviors like event listeners.
Overview
The template binding pipeline has five major stages:
- Template Authoring – The
htmltagged template collects binding expressions and builds placeholder-marked HTML. - Compilation – The compiler parses the placeholder HTML into a
DocumentFragment, walks the DOM tree, and records where each binding targets. - View Creation – The compiled result clones the fragment and creates a targets object that maps each binding to its DOM node.
- Binding (first render) – Each factory creates a behavior that attaches to its target node: adding event listeners, setting attributes, or observing expressions.
- Reactive Updates – When observed data changes, one-way binding observers notify their directive, which re-evaluates the expression and pushes the new value to the DOM through a "sink" function.
Mermaid Diagrams
1. Template Creation & Storage
This diagram shows how the html tagged template literal processes interpolated values and stores them as factories keyed by unique IDs.
flowchart TD
A["html`<div class='${expr}'>${text}</div>`"] --> B["ViewTemplate.create(strings, values)"]
B --> C{"For each interpolated value"}
C -->|Function| D["Wrap in oneWay() → HTMLBindingDirective"]
C -->|Binding instance| E["Wrap in HTMLBindingDirective"]
C -->|HTMLDirective| F["Use directly"]
C -->|Static value| G["Wrap in oneTime() → HTMLBindingDirective"]
D --> H["directive.createHTML(add)"]
E --> H
F --> H
G --> H
H --> I["add(factory) assigns unique ID"]
I --> J["factories Record<string, ViewBehaviorFactory>"]
I --> K["Returns placeholder marker in HTML string"]
K --> L["Aspect Detection via lastAttributeNameRegex"]
L -->|In attribute context| M["HTMLDirective.assignAspect(directive, attrName)"]
L -->|In text content| N["aspectType = DOMAspect.content"]
M --> O{"Attribute prefix"}
O -->|No prefix| P["DOMAspect.attribute"]
O -->|: prefix| Q["DOMAspect.property"]
O -->|? prefix| R["DOMAspect.booleanAttribute"]
O -->|"@" prefix| S["DOMAspect.event"]
O -->|:classList| T["DOMAspect.tokenList"]
J --> U["new ViewTemplate(html, factories)"]2. Compilation – Walking the DOM & Storing Target Locations
The compiler transforms the placeholder-laden HTML into a DocumentFragment and builds a prototype with lazy property descriptors that navigate to target nodes by child index.
/* Detailed source-code truncated for AI context efficiency. */3. View Creation – Cloning the Fragment & Resolving Targets
flowchart TD
A["compilationResult.createView(hostBindingTarget?)"] --> B["fragment.cloneNode(true) → new DocumentFragment"]
B --> C["targets = Object.create(proto)"]
C --> D["targets.r = fragment (root)"]
D --> E["targets.h = hostBindingTarget (host element)"]
E --> F{"For each registered nodeId"}
F --> G["Access targets[nodeId] — triggers lazy getter chain"]
G --> H["Getter resolves: this[parentId].childNodes[childIndex]"]
H --> I["Caches result in targets._nodeId field"]
I --> J["new HTMLView(fragment, factories, targets)"]
subgraph "HTMLView Structure"
K["fragment: DocumentFragment with cloned DOM"]
L["factories: CompiledViewBehaviorFactory[]"]
M["targets: ViewBehaviorTargets {nodeId → Node}"]
N["firstChild / lastChild: boundary nodes"]
O["source: null (until bound)"]
P["behaviors: null (created on first bind)"]
end
J --> K
J --> L
J --> M4. Binding – Attaching Behaviors to DOM Nodes
flowchart TD
A["view.bind(source)"] --> B{"First bind?"}
B -->|Yes| C["Set view.source = source"]
C --> D["Create behaviors array"]
D --> E{"For each factory"}
E --> F["factory.createBehavior()"]
F --> G["Select sink from sinkLookup based on aspectType"]
G --> H["Wrap sink through policy.protect()"]
H --> I["behavior.bind(controller/view)"]
B -->|Subsequent| J["Unbind previous, set new source"]
J --> K["Re-bind existing behaviors"]
I --> L{"aspectType?"}
L -->|DOMAspect.event| M["Store controller ref on target node"]
M --> N["target.addEventListener(targetAspect, directive, options)"]
N --> O["directive IS the EventListener via handleEvent()"]
L -->|DOMAspect.content| P["Register unbind handler"]
P --> Q["Create observer via dataBinding.createObserver()"]
L -->|attribute / property / etc.| Q
Q --> R["observer.bind(controller) → evaluates expression"]
R --> S["updateTarget(target, aspect, value, controller)"]
subgraph "Sink Functions (sinkLookup)"
S1["attribute → DOM.setAttribute(target, name, value)"]
S2["booleanAttribute → DOM.setBooleanAttribute(target, name, value)"]
S3["property → target[name] = value"]
S4["content → updateContent() — manages text or composed views"]
S5["tokenList → updateTokenList() — classList with versioning"]
S6["event → no-op (handled via addEventListener)"]
end5. Reactive Update Cycle – Change Detection & DOM Updates
sequenceDiagram
participant Source as Data Source
participant Observable as Observable System
participant Observer as ExpressionObserver (OneWayBinding)
participant Directive as HTMLBindingDirective
participant Sink as UpdateTarget (sink fn)
participant DOM as Target DOM Node
Note over Source,DOM: Initial Bind
Directive->>Observer: dataBinding.createObserver(directive, directive)
Observer->>Source: Evaluate expression (tracks property access)
Source-->>Observer: Return value + record dependencies
Directive->>Sink: updateTarget(target, aspect, value, controller)
Sink->>DOM: Apply value (setAttribute, textContent, etc.)
Note over Source,DOM: Data Change
Source->>Observable: Property setter triggered
Observable->>Observer: Notify subscriber of change
Observer->>Directive: handleChange(binding, observer)
Directive->>Observer: observer.bind(controller) — re-evaluate
Observer->>Source: Re-evaluate expression
Source-->>Observer: Return new value
Directive->>Sink: updateTarget(target, aspect, newValue, controller)
Sink->>DOM: Update DOM with new value6. Event Handling – Click Events and Other DOM Events
sequenceDiagram
participant User as User Interaction
participant DOM as Target DOM Element
participant Directive as HTMLBindingDirective (EventListener)
participant Context as ExecutionContext
participant Binding as dataBinding.evaluate()
participant Source as Component Source
Note over DOM: During bind: target.addEventListener("click", directive)
Note over DOM: directive stores controller ref on target[data]
User->>DOM: Click event fires
DOM->>Directive: handleEvent(event)
Directive->>DOM: Read controller from event.currentTarget[data]
Directive->>Directive: Check controller.isBound
Directive->>Context: ExecutionContext.setEvent(event)
Directive->>Binding: evaluate(controller.source, controller.context)
Binding->>Source: Execute handler: (s, c) => s.handleClick(c.event)
Source-->>Binding: Return result
Binding-->>Directive: Return result
alt result !== true
Directive->>DOM: event.preventDefault()
end
Directive->>Context: ExecutionContext.setEvent(null)7. Content Binding – Template Composition
When a binding expression returns a ContentTemplate (e.g., another ViewTemplate), the content update sink composes a child view into the DOM.
flowchart TD
A["updateContent(target, aspect, value, controller)"] --> B{"value is null/undefined?"}
B -->|Yes| C["Treat as empty string"]
B -->|No| D{"value.create exists? (ContentTemplate)"}
D -->|Yes — Template| E["Clear target.textContent"]
E --> F{"Existing view on target.$fastView?"}
F -->|No| G["view = value.create()"]
F -->|Yes, same template| H["Reuse existing view"]
F -->|Yes, different template| I["Remove old view, create new"]
G --> J["view.bind(source, context)"]
I --> J
J --> K["view.insertBefore(target)"]
K --> L["Cache: target.$fastView = view, target.$fastTemplate = value"]
D -->|No — Primitive| M{"Existing composed view?"}
M -->|Yes| N["view.remove(), view.unbind()"]
N --> O["target.textContent = value"]
M -->|No| OKey Data Structures
| Structure | Location | Purpose |
|---|---|---|
ViewTemplate |
template.ts | Holds raw HTML + factories record. Entry point for compilation. |
factories: Record<string, ViewBehaviorFactory> |
template.ts | Maps unique IDs to binding factories (directives). |
CompilationContext |
compiler.ts | Accumulates factories and builds the target prototype during compilation. |
targets: ViewBehaviorTargets |
view.ts / html-directive.ts | Maps node IDs (e.g., "r.0.2") to actual DOM nodes in a cloned fragment. |
HTMLView |
view.ts | The live view instance: holds the fragment, factories, targets, behaviors, and source. |
HTMLBindingDirective |
html-binding-directive.ts | The core binding: acts as factory, behavior, and event listener. |
sinkLookup |
html-binding-directive.ts | Maps DOMAspect types to DOM update functions. |
Binding (abstract) |
binding/binding.ts | Wraps an expression with policy, volatility, and observer creation. |
ExpressionObserver |
observation/observable.ts | Tracks dependencies during expression evaluation and notifies on change. |
Binding Type Summary
| Markup Syntax | Aspect Type | Sink Function | Example |
|---|---|---|---|
attr="${x => x.val}" |
attribute |
DOM.setAttribute |
class="${x => x.cls}" |
?attr="${x => x.val}" |
booleanAttribute |
DOM.setBooleanAttribute |
?disabled="${x => x.off}" |
:prop="${x => x.val}" |
property |
target[prop] = value |
:value="${x => x.name}" |
:classList="${x => x.val}" |
tokenList |
updateTokenList |
:classList="${x => x.classes}" |
@event="${x => x.handler}" |
event |
addEventListener | @click="${(x,c) => x.onClick(c.event)}" |
${x => x.val} (in text) |
content |
updateContent |
${x => x.msg} |
Hydration: Attaching Bindings to Server-Rendered DOM
When a page is server-side rendered (SSR) with Declarative Shadow DOM, the HTML arrives in the browser fully formed. Instead of creating new DOM nodes, FAST's hydration system reuses the existing DOM and attaches reactive bindings to it. This section explains how hydration markers in the SSR output guide the client-side binding process.
Enabling Hydration
Hydration is an opt-in feature. Calling enableHydration() patchesViewTemplate.prototype with:
- A
Hydratablesymbol — marks the template as hydration-capable (checked viaisHydratable()). - A
hydrate(firstChild, lastChild, hostBindingTarget?)method — creates aHydrationViewinstead of anHTMLView.
import { enableHydration } from "@microsoft/fast-element/hydration.js";
enableHydration();Hydration Marker Format
The SSR renderer embeds comment nodes and data attributes into the HTML to mark where bindings should attach. These markers encode the factory index (position in the compiled factories array) so the client can map each marker back to its corresponding ViewBehaviorFactory.
Attribute Binding Markers
Elements with attribute/property/event bindings receive a data-fe attribute with the binding count:
<div data-fe="2">server-rendered content</div>The attribute marker format is:
| Attribute | Example | Description |
|---|---|---|
data-fe |
data-fe="3" |
Binding count — the number of attribute bindings on the element |
Content Binding Markers
Text/template content bindings are wrapped in paired data-free comment nodes (matched by string equality, paired by balanced depth counting):
Hello, World!Repeat Directive Markers
Each repeated item is bracketed by data-free repeat markers:
<li>First item</li>
<li>Second item</li>Element Boundary Markers
Nested custom elements that also need hydration are demarcated so the parent's walker can skip over them:
<child-element>
<template shadowrootmode="open">...child shadow DOM...</template>
</child-element>Hydration Binding Flow
hydration/traversal.ts owns pointer navigation and its live Range fallback.hydration/target-builder.ts consumes that traversal to map factories, keeping
direct native marker reads inline. HydrationView uses the same range utility
when producing diagnostic fragments, without coupling it to target discovery.
enableHydration() replaces the small core components/element-registration.ts
strategy with hydration/attribute-observation.ts. Client-only FASTElement
bundles do not include observation tracking. Immediately before FAST's platform
registration, tracking records only a boolean proving that data-fe is
unobserved, after extensions and asynchronous template resolution. The proof is
available during synchronous upgrades and restored if registration fails.
Earlier untracked registrations remain conservative; calling enableHydration()
again does not reset existing proof. Hydration never reconstructs the platform
snapshot by rereading constructor.observedAttributes.
For a known unobserved data-fe attribute and native attribute methods, removal
cannot invoke an attribute-change callback, so pointers remain active.
Registration/lifecycle accessors, custom attribute iterators, unknown registry
wrappers, customized built-ins, and overridden attribute methods remain
conservative. Legacy/custom marker cleanup still captures live boundaries from
the outset. Marker removal is not deferred and callback order is unchanged.
/* Detailed source-code truncated for AI context efficiency. */HydrationView vs HTMLView
flowchart LR
subgraph "HTMLView (client-rendered)"
A1["Compiler produces DocumentFragment"] --> A2["fragment.cloneNode(true)"]
A2 --> A3["Resolve targets via prototype getter chain"]
A3 --> A4["Create behaviors & bind"]
end
subgraph "HydrationView (server-rendered)"
B1["DOM already exists in shadow root"] --> B2["Bounded pointer DFS scans markers; live Range fallback when needed"]
B2 --> B3["buildViewBindingTargets maps markers → nodes"]
B3 --> B4["Create behaviors & bind to existing nodes"]
B4 --> B5["Marker comments cleared (data set to empty string)"]
end| Aspect | HTMLView | HydrationView |
|---|---|---|
| DOM source | Clones compiled DocumentFragment | Reuses server-rendered DOM in place |
| Target resolution | Prototype getters via childNodes indices | Pointer DFS + sequential markers; live Range / generic-strategy fallback |
| Node creation | Creates all DOM nodes from scratch | No node creation (reuses existing) |
| Fragment | Holds cloned fragment, moved into host | No fragment initially (created only on remove) |
| Lifecycle | Ready immediately after creation | Transitions through unhydrated → hydrating → hydrated |
| Validation | Compilation guarantees structure | Must validate markers match factories (throws HydrationBindingError) |
| Boundaries | Not needed (compiler tracks structure) | bindingViewBoundaries stores first/last node pairs for structural directives |
Content Binding Hydration
When HTMLBindingDirective.bind() runs during hydration and the binding returns a ContentTemplate, the updateContent sink checks for pre-rendered boundaries:
sequenceDiagram
participant Directive as HTMLBindingDirective
participant Controller as HydrationView (controller)
participant Sink as updateContent()
participant Template as ContentTemplate
Directive->>Controller: bind(controller)
Note over Directive: aspectType = DOMAspect.content
Directive->>Sink: updateTarget(target, aspect, value, controller)
Sink->>Sink: value is ContentTemplate?
alt Hydrating & boundaries exist
Sink->>Controller: Check bindingViewBoundaries[targetNodeId]
Controller-->>Sink: { first: Node, last: Node }
Sink->>Template: value.hydrate(first, last)
Template-->>Sink: HydrationView (reuses existing nodes)
Sink->>Sink: view.bind(source, context) — attaches to pre-rendered DOM
else Already hydrated or no boundaries
Sink->>Template: value.create()
Template-->>Sink: HTMLView (creates new nodes)
Sink->>Sink: view.bind + view.insertBefore
endRepeat Directive Hydration
The repeat directive scans forward from its own content boundary to its
location, pairing top-level ranges with a stack. This avoids reverse collection
and rescanning prefixes belonging to earlier adjacent repeats. Nested markers
remain untouched until child views hydrate. Both unmatched starts and unmatched
ends throw before repeat markers are cleared.
The read-only collectHydrationRepeatRanges() helper inhydration/repeat-ranges.ts owns this discovery and validation. RepeatBehavior
supplies its own binding boundaries, then clears top-level markers and hydrates,
creates, or removes views. The discovery helper does not alter marker data or
bind views.
flowchart TD
A["RepeatBehavior.bind(controller)"] --> B{"isHydratable(template) && hydrating?"}
B -->|Yes| C["hydrateViews(template)"]
B -->|No| D["refreshAllViews() — normal client path"]
C --> E["Allocate views array sized to items.length"]
E --> F["Scan bindingViewBoundaries[targetNodeId].first through location"]
F --> G{"Repeat marker?"}
G -->|Start| H["Push start marker"]
G -->|End| I["Pop start; throw if absent"]
I --> J{"Stack empty?"}
J -->|Yes| K["Append top-level range in item order"]
J -->|No| F
H --> F
K --> F
G -->|Other node| F
F -->|Reached location| L["Throw if stack nonempty; otherwise clear top-level markers"]
L --> M["Hydrate overlapping SSR/client ranges in order"]
M --> N["Create missing client views and remove excess server ranges"]
subgraph "Result"
S["Each array item mapped to a HydrationView"]
T["Views reuse server-rendered DOM between markers"]
U["Markers cleared to empty strings (invisible in DOM)"]
endHydration CPU regression coverage
src/hydration/hydration-performance.pw.spec.ts instruments Range/TreeWalker
creation and native factory-index array allocation across 500 sibling-bounded
views. Those counters must all be zero on the ordinary native path. It also
asserts exact linear repeat predicate visits (four starts and two ends per
four-comment nested item), scope isolation from earlier siblings, nested marker
preservation, malformed-marker diagnostics, installed markers_v2, empty items,
and missing/excess reconciliation. Existing target-builder tests compare live
endpoint mutations with the legacy Range walker. The declarative repeat fixture
checks SSR node identity, three nested repeat levels, an empty inner list, and an
adjacent repeat before exercising reactive updates. The co-locatedsrc/hydration/repeat-ranges.pw.spec.ts also verifies that successful discovery
leaves native and legacy marker data unchanged and excludes adjacent ranges.
src/hydration/native-marker-cleanup.pw.spec.ts exercises a 1,000-record,
three-repeat-level workload with 20 adjacent prefix items: 4,021 FAST elements,
4,020 native attribute markers, and 8,041 hydrated views. Range/TreeWalker setup,comparePoint, and intersectsNode counts are all zero; all 7,020 SSR descendant
elements retain their identity. Mutation fixtures require the fallback and
verify immediate insertion/removal, endpoint moves, unstable observation
metadata, method accessors, customized built-ins, and legacy/custom strategies.
Registration-order cases cover disabled tracking, enabling after registration,
repeated enablement after observation changes, and platform registration deferred
until after asynchronous template resolution.
The declarative repeat fixture includes bound FAST child elements, checks zero
live traversal setup, and exercises reactive updates after hydration.
Run focused correctness coverage from the monorepo root:
npm exec -w @microsoft/fast-element -- playwright test src/hydration/target-builder.pw.spec.ts src/hydration/hydration-performance.pw.spec.ts src/hydration/native-marker-cleanup.pw.spec.ts src/hydration/repeat-ranges.pw.spec.ts src/templating/repeat.pw.spec.ts --project=chromiumCPU timings live separately in src/hydration/hydration-benchmark.pw.spec.ts
and are opt-in rather than flaky wall-clock CI gates:
FAST_HYDRATION_BENCHMARK=1 npm exec -w @microsoft/fast-element -- playwright test src/hydration/hydration-benchmark.pw.spec.ts --project=chromium --retries=0The workloads measure 4,000 marker-free sibling views, 4,000 native
attribute/content marker groups, 4,000 nested repeat ranges with stubbed child
hydration, 20 adjacent repeats with 200 flat item ranges each, and a combined
1,000-record page with three repeat levels, 4,000 minimal native custom elements,
and 8,001 hydrated views. Setup, custom-element registration, and template
construction are outside the timed interval; repeat workloads include bind and unbind. Each
workload has one warm-up and ten recorded samples, each in a fresh document so
live Ranges from earlier samples cannot amplify later DOM mutation costs.
Compare the same test on the base and changed revisions on the same browser and
machine. To attribute improvements, also compare traversal-only, native-only,
and repeat-only source variants in an isolated worktree, reversing variant order
for a second pass. The combined workload includes conservative live-Range
fallbacks for custom-element attribute cleanup, not just ordinary element views.
These microbenchmarks time template hydration, not the issue's application
registration or hydration-completion tracking, and should not be presented as
that application's hydration timings.
The separate native FAST workload times bind/unbind for the 4,021-element /
8,041-view case above. Setup and registration are excluded, identity and marker
removal are verified after every sample, and the same fresh-document sampling
rules apply. Use this workload to compare the known-unobserved cleanup path;
the original combined workload intentionally registers ordinary custom elements
outside FAST and therefore retains its conservative fallback.
Hydration Stage Lifecycle
stateDiagram-v2
[*] --> unhydrated: HydrationView created
unhydrated --> hydrating: bind() called
note right of hydrating
buildViewBindingTargets() scans DOM
Markers parsed, targets resolved
Behaviors created and bound
Attribute bindings skip DOM update
(server already set correct values)
end note
hydrating --> hydrated: All behaviors bound
note right of hydrated
Subsequent bind() calls
behave like normal HTMLView rebind
(re-evaluate expressions, update DOM)
end note
hydrated --> hydrated: rebind with new sourceDuring the hydrating stage, attribute and boolean-attribute bindings skip their initial DOM update (the server already rendered the correct value). This avoids unnecessary DOM writes during hydration:
// In HTMLBindingDirective.bind(), during hydration:
if (isHydrating && (this.aspectType === DOMAspect.attribute ||
this.aspectType === DOMAspect.booleanAttribute)) {
observer.bind(controller); // Set up observation only
break; // Skip updateTarget — server value is current
}Error Handling
When the server-rendered DOM doesn't match the client template, hydration throws descriptive errors:
| Error | Cause | Contains |
|---|---|---|
HydrationTargetElementError |
data-fe specifies a binding count that cannot be satisfied, more content binding markers exist than factories, or an element boundary end marker is missing |
Factories array, target node; with hydrationDebugger() installed: structured expected (string or HydrationMismatchExpectation) describing what the walk wanted next, and received (HydrationMismatchActual) containing an HTML snippet of the offending DOM |
HydrationBindingError |
A factory's targetNodeId has no matching entry in targets after the SSR DOM walk completes |
Factory, cloned fragment, template string; with hydrationDebugger() installed: structured expected (HydrationMismatchExpectation with tagName and human-readable aspect) and received (HydrationMismatchActual with an HTML snippet of the SSR view range) |
HydrationRepeatError |
A repeat view slot is unexpectedly null / undefined during rebind or unbind (internal invariant violation). SSR/client item-count mismatches are no longer surfaced as errors — they are reconciled silently by RepeatBehavior.hydrateViews() (missing client views are created, extra SSR ranges are removed) |
Hydration stage, items length, view states |
FAST.error(1210) |
data-fe attribute contains a non-numeric or non-positive value |
Attribute value |
These errors typically indicate a mismatch between the server-rendered HTML and the client-side template definition.
By default, both errors emit a minimal one-line message that points at the opt-in hydrationDebugger(). Install the debugger from @microsoft/fast-element/hydration.js and pass it to enableHydration to swap in the rich format:
import { enableHydration, hydrationDebugger } from "@microsoft/fast-element/hydration.js";
enableHydration({ debugger: hydrationDebugger() });With the debugger installed, both errors format their message as:
Hydration mismatch in <host-element>.
Expected: <tag> with <aspect> binding (or a structural description)
Received: <SSR HTML snippet>and expose the same information programmatically through the expected and received fields so tooling can read the structured description without parsing the message string. Keeping the debugger out of the import graph saves ~0.5 KB on the production hydration bundle.
Packages Fast Router Docs Api Report.Api (`packages/fast-router/docs/api-report.api.md`)
API Report File for "@microsoft/fast-router"
Do not edit this file. It is a report generated by API Extractor.
/* Detailed source-code truncated for AI context efficiency. */Packages Fast Build Docs Migration Plugin Integrations (`packages/fast-build/docs/migration/plugin-integrations.md`)
Migrating plugin integration pipelines
Structured errors
WASM failures now throw JavaScript Error objects rather than strings. Readerror.message for the previous human-readable text; String(error) now has the
standard Error: prefix. Replace message matching with stable error.code
checks. Both WASM declaration modules export a FastBuildError interface extendingError; it is a type only, not an exported constructor.
Optional line/column are one-based UTF-16 coordinates, and length is a UTF-16
error-token highlight length. CRLF is one break; lone CR/LF also break lines.
Entry and conversion coordinates refer to the complete supplied HTML. A named
custom-element render failure instead refers to its separately supplied content.
Use templateName to select that source, rather than attaching its coordinates
to the entry or to a full file from which content was extracted. Older metadata
does not carry enough provenance to reconstruct those full-file positions.
Do not fabricate HTML locations when these fields are absent (JSON/options,
missing sources, filesystem errors, or separate CSS).
Native Rust RenderError and ConvertError expose code(), location() returning
an optional ErrorLocation, template_name(), and kind(). Source-annotated
validation errors may use the new Located variant; match error.kind() to
inspect the underlying variant. Context fields now use boxed ErrorContext
instead of String; use context.to_string() to read the unchanged snippet andString::into() when constructing a context without source provenance.
Update exhaustive enum matches for Located. These are intentional pre-1.0
native API changes. Successful rendering/conversion, warnings, missing-state
behavior, and permissive parse_f_templates parsing are unchanged.
Attribute output changes
Missing and explicit null bindings now omit the entire attribute, including
mixed literal/binding values. To intentionally keep href="", provide ""
instead of null. JSON booleans on known HTML boolean attributes use presence:
false omits disabled, true emits it. ARIA/ordinary attribute booleans remain
textual. Use FAST's ?name syntax for matching client-side truthy/falsy behavior.
Author bindings resolving to absence suppress matching template-host defaults.
Remove wrapper regular expressions that strip empty values: they would erase
intentional empty strings. Hydration slot counts remain unchanged.
Typed converter output
The default output remains unchanged. Opt into typed: true (or --typed)
for explicit ElementViewTemplate<TSource> / ElementStyles annotations and
type-only imports, then remove wrapper string replacements. Supplytype_source/--type-source when strict source-property checking is required.
Rust ConvertOptions struct literals must include the new typed field or
use ..ConvertOptions::default().
The pre-1.0 native error enums also gain integration-related variants; update
exhaustive RenderError / ConvertError matches accordingly.
Caller-owned host markup
Replace throwaway-host rendering and substring extraction withrender_shadow_root (WASM) or render_shadow_root_*_with_locator (Rust).
Pass resolved component state and insert the returned complete DSD <template>
into the host you own. The fragment does not include host/default attributes
or light-DOM children.
Persisted template metadata
parse_f_templates keeps its JSON array shape and existing fields, adding
per-record formatVersion: 1 and bindings. Preserve these fields when
persisting or constructing a keyed map. Unsupported/malformed explicit versions
now fail before rendering. Unversioned legacy maps remain supported, but cannot
receive stale-version protection. Regenerate metadata on format changes rather
than rewriting a version number onto incompatible data.
Bindings describe names, expression kinds, paths, and repeat scopes. Unknown
types and state optionality are explicitly null, not inferred declarations.
Repeat collections alone have a known array requirement. Complex/client
references are lexical descriptions; metadata is not a full state schema or
syntax-validation certificate. Use application declarations for definitive
type/requiredness checks.
Each newly parsed binding now includes an authored span with half-open start
and end positions in the complete input string. Offsets are zero-based UTF-16;
lines and UTF-16 columns are one-based. Use the original input, not the returned
trimmed content, when slicing or attaching diagnostics. The range covers the
trimmed expression without braces; repeat bindings locate only the iterable.
CRLF counts as one line break and two offset units; lone CR and LF each count
as a line break. Raw attribute entities are not decoded.
Format version 1 is unchanged, and renderers still accept older records without
spans. Regenerate persisted metadata to obtain authored locations; preserve spans
when mapping or serializing records. Native BindingMetadata struct literals
must now supply the public span: SourceSpan field; prefer parse_f_templates
to constructing descriptive metadata manually. Generated WASM declarations exposeTemplateSourceSpan and TemplateSourcePosition.
Packages Fast Element Docs Architecture Fastelement (`packages/fast-element/docs/architecture/fastelement.md`)
FASTElement
The FASTElement is our extension of the HTMLElement. As such it leverages the lifecycles but also requires additional setup before before the class constructor or the connectedCallback method is executed which are explained in the overview. This document explains specifically what FASTElement leverages inside the HTMLElements lifecycle hooks.
For an explanation into HTMLElement lifecycle hooks refer to the MDN documentation.
Because FASTElement extends HTMLElement, it is a browser Window runtime
API. It is not intended to execute in workers, server-side JavaScript runtimes,
or other non-window hosts where custom element lifecycle callbacks and animation
frame scheduling are unavailable.
A Custom Element is Detected by the Browser
Because the FASTElement is an extension of the HTMLElement, it makes use of the same lifecycle hooks and extends them with $fastController. It also initializes using another class ElementController, the methods this are called during the custom elements native constructor, connectedCallback, disconnectedCallback, and attributeChangedCallback lifecycle hooks.
🔄 **Lifecycle**: Initialization
flowchart TD
A[A <code>FASTElement</code> web component is added to the <code>DOM</code>] --> B
B[<code>FASTElement</code> initializes the <code>ElementController.forCustomElement</code> passing the Custom Element instance] --> C
B --> F[Observables applied to the <code>FASTElement</code> are updated on the FAST global without values]
C{Is an <code>ElementController</code> available?}
C --> |yes|E
C --> |no|D
D[Initialize a new <code>ElementController</code> referred to as setting an element controller strategy]
D --> F
E[<code>ElementController</code> captures the Custom Element instance and the definition and attaches them to the <code>$fastController</code> which is then attached to the instance]🔄 **Lifecycle**: Component is connected
flowchart TD
A[browser <code>HTMLElement</code>'s <code>connectedCallback</code> is called] --> B
B[<code>this.$fastController.connect</code> in FASTElement is called] --> C
B --> D
B --> E
B --> F
C[bind observables by capturing public own Custom Element properties and setting those values through the matching accessors]
D[connect behaviors by call the <code>connectedCallback</code> on all behaviors]
E[render template, execute an <code>ElementViewTemplate</code>'s render method]
F[add styles either as an appended <code>StyleElement</code> node or an <code>adoptedStylesheet</code> which may include and attach behaviors]Render Template
The rendering of the template on the ElementController is by the renderTemplate method which is called during the ElementController.connect method which is triggered by HTMLElements connectedCallback lifecycle.
The renderTemplate identifies the Custom Element, and the shadow root associated with the Custom Element. This then places a rendering of the template (an ElementView) onto the internal view of the controller. When creating the ElementView/HTMLView using the ViewTemplate.render, the Compiler.compile() method identifies a DocumentFragment either by using an existing <template> tag, or creating one to wrap the contents of the shadow root. A new CompilationContext is created and the compileAttributes function is called, this results in the replacement of the placeholder attributes initally set-up during the pre-render step with their values if a value has been assigned. The factories with the associated nodes identified are then passed to the context. The view then binds all behaviors to the source element. The CompilationContext.createView is executed with the DocumentFragment as the root, and returns an HTMLView. This HTMLView includes an appendTo method to attach the fragment to the host element, which it then does. It should be noted that the compiled HTML is a string, which when set on the DocumentFragment as innerHTML, this allows the browser to dictate the creation of HTML nodes.
🔄 **Lifecycle**: Component is disconnected
When a component is disconnected, a cleanup step is created to remove associated behaviors.
🔄 **Lifecycle**: Attribute has been changed
Attributes have an AttributeDefinition which allows for converters, attachment of the <attributName>Changed aspect of the @attr decorator among other capabilities.
flowchart TD
A[The browser <code>HTMLElement</code>'s <code>attributeChangedCallback</code> is called] --> B
B[<code>this.$fastController.onAttributeChangedCallback</code> in <code>FASTElement</code> is called] --> C
C[calls the attribute definitions <code>onAttributeChangedCallback</code> method with the updated value] --> D
D[An <code>Updates.enqueue</code> is called which places the update in the task queue which is then executed, these are performed async unless otherwise specified]These changes are observed and similar to the way Observables work, they utilize an Accessor pattern which has a getValue and setValue in which DOM updates are applied.
Observable backing slots such as _foo are FAST-owned internal state. The
controller's connection-time observable binding only replays public own
properties that shadow prototype accessors; it does not normalize backing slots
into public property assignments. This keeps attribute converter output in its
already-converted model form while preserving pre-upgrade public property
assignments and opt-in extension behavior.
Packages Fast Element Docs Architecture Html Tagged Template Literal (`packages/fast-element/docs/architecture/html-tagged-template-literal.md`)
`html` tagged template literal
The html export from @microsoft/fast-element is used to create the template logic for the custom element.
Pre-processing the `html` tagged template literal contents
Before the template can be used it goes through a step to convert it into a ViewTemplate which it does via the ViewTemplate.create() method. This is then used during the compose step, before FASTElement is instantiated.
During the static ViewTemplate.create() method (called by the html tag), the following happens for each string:
- Factories with unique IDs are created for each tag template literal argument (or
TemplateValue) which matches with the corresponding string - A binding is created from the
TemplateValue
The Compiler.compile() method is called lazily the first time the template is rendered (via ViewTemplate.compile(), which is triggered by ViewTemplate.create() instance method or ViewTemplate.render()).
A resulting string using a createHTML() function is produced using the HTMLDirectives executed for each factory. The behavior is augmented by the previous string from the html tag template which determines the aspect if one exists, these aspects are the @, :, or other binding aspect attached to attributes.
The createHTML() function utilizes a Markup attribute which is assigned to a factory's unique ID. The strings are concatenated and passed to a new ViewTemplate with all the factories (empty until one is assigned) that act as a dictionary with the unique IDs as the key to look up each factory once it has been created. The string this creates is injected into a <template> as innerHTML, which allows the browser to create the nodes and placeholder factory IDs, with the only DOM node that is explicitly created being the wrapping <template> element.
Directives
The HTMLBindingDirective applies bindings to items identified as various TemplateValues within the html tagged template. The createHTML step uses the factory associated with the binding to create strings in the markup using the factory's ID.
flowchart TD
A[A <code>new HTMLBindingDirective</code> is created with a data binding which has a policy, options, <code>createObserver</code>, and an evaluate method assigned to the passed arrow function such as <pre>x => x.foo</pre>]
B[<code>oneTime</code> binding passes the corresponding tag template argument, an arrow function]
C[<code>oneWay</code> binding passes a copy of the corresponding tag template argument, an arrow function]
D[An already specified binding such as a <code>repeat</code> or <code>when</code> directive is passed]
A --> B
A --> C
A --> D
E[When a <code>createObserver</code> is called, an <code>Observable.binding</code> is created passing the arrow function to be evaluated and the subscriber to be notified]
C --> E
F[When a <code>createObserver</code> is called, the instance of the one time binding is returned which includes a bind method returning the arrow function executed against the controller source and context]
B --> FPackages Fast Element Docs Architecture Updates (`packages/fast-element/docs/architecture/updates.md`)
`Updates`
The Updates API batches FAST Element DOM work in the client-side browserWindow runtime. Queued work includes updates to attributes, observables, and
observed arrays. The async queue is aligned to requestAnimationFrame, which is
expected to be available in the supported runtime.
FAST Element is not intended to run its client runtime in workers, server-side
JavaScript runtimes, or other non-window hosts. Code that needs server rendering
should generate HTML for the browser, then let the browser window run FAST
Element, connect custom elements, and process queued updates.
Attributes
An attribute change may be queued when the value of the attribute is updated. This change is triggered by HTMLElement lifecycle hook attributeChangedCallback. The Updates queue is used to try to reflect the attribute with the updated value onto the element using DOM APIs.
Observables
The Reflect API is used to override defineProperty in order to observe a property on an Custom Element, this overrides the getter and setter, which allows subscribers to be notified of changes. Any changes which set the value are passed to the Updates queue.
Packages Fast Element Docs Arrays Api Report.Api (`packages/fast-element/docs/arrays/api-report.api.md`)
API Report File for "@microsoft/fast-element"
Do not edit this file. It is a report generated by API Extractor.
/* Detailed source-code truncated for AI context efficiency. */Packages Fast Element Docs Context Api Report.Api (`packages/fast-element/docs/context/api-report.api.md`)
API Report File for "@microsoft/fast-element"
Do not edit this file. It is a report generated by API Extractor.
/* Detailed source-code truncated for AI context efficiency. */Packages Fast Element Docs Declarative Api Report.Api (`packages/fast-element/docs/declarative/api-report.api.md`)
API Report File for "@microsoft/fast-element"
Do not edit this file. It is a report generated by API Extractor.
/* Detailed source-code truncated for AI context efficiency. */Packages Fast Element Docs Declarative Design (`packages/fast-element/docs/declarative/design.md`)
Declarative HTML Design
This document is intended for contributors who want to understand the internal
architecture of the declarative runtime and schema-driven map extensions in@microsoft/fast-element. It covers the feature's purpose, core concepts, data
flow, and its integration with the rest of @microsoft/fast-element.
Table of Contents
- Overview
- Goals
- Core Concepts
- Package Structure
- Exports and Public API
- Template Syntax
- Data Flow
- Template Parsing Pipeline
- Schema and Observer Map
- Lifecycle
- Integration with fast-element
- Hydration Model
- Testing Architecture
- Further Reading
Overview
@microsoft/fast-element lets you write FAST Web Component
templates as plain HTML rather than JavaScript html tagged template literals.
The browser-side JS bundle defines FAST's internal native <f-template>
publisher on demand. It parses declarative template markup at runtime and
returns a ViewTemplate to the waiting FAST element definition through the
registry-aware declarative template bridge.
<my-component greeting="Hello">
<template shadowrootmode="open">
Hello
</template>
</my-component>
<f-template name="my-component">
<template>{{greeting}}</template>
</f-template>Goals
| Goal | Description |
|---|---|
| Server-agnostic rendering | Templates are plain HTML strings with no dependency on Node.js or any specific SSR framework. |
| Progressive enhancement | Components can be server-rendered and then hydrated client-side without a full re-render. |
| FAST parity | The declarative syntax maps 1-to-1 to FAST Element directive helpers (repeat, when, slotted, children, ref) exported from @microsoft/fast-element. |
| Minimal authoring overhead | Component authors write HTML, not tagged template strings, while retaining full reactive capabilities. |
Core Concepts
`` — the internal template publisher
<f-template> is an internal custom element implemented as a lightweight nativeHTMLElement. It is defined automatically by declarativeTemplate() in the sameCustomElementRegistry as the FAST element definition. Consumers should not
import, subclass, or define the implementation directly.
When connected to the DOM it:
- Registers itself with the declarative template bridge using its registry and
nameattribute. - Publishes a template when a matching FAST element definition requests one.
- Delegates parsing of the inner
<template>tag toTemplateParser, which
converts declarative bindings into FASTViewTemplatestrings and values. - Runs definition-scoped schema transforms, such as
attributeMap()andobserverMap(), before returning the concreteViewTemplate.
When multiple connected <f-template> publishers share the same name, the
bridge resolves pending definitions from the first connected publisher and keeps
the resolved template stable. Later duplicate publishers do not reassign the
definition template.
`TemplateParser` — declarative HTML parser
A standalone class that converts declarative HTML template markup into thestrings and values arrays that ViewTemplate.create() consumes. It is used
by the internal <f-template> publisher but can also be used independently for
programmatic template compilation. The parsing pipeline is fully synchronous —
no promises are allocated during template resolution. A StringsAccumulator
tracks the running concatenation of preceding HTML, eliminating repeated O(N)join("") calls at each binding site. createTemplate() lazily installs the
declarative runtime's debug messages; hydration support is installed only byenableHydration().
`Schema` — JSON schema builder
Built during declarative template parsing, one Schema instance per<f-template>. Non-declarative callers can also create and attach a Schema
manually. It records every binding path discovered in the template or supplied
by code and constructs a JSON Schema-compatible data structure. This schema:
- Describes the shape of each root property referenced in the template.
- Tracks repeat context chains (parent/child array relationships).
- Uses an instance-level
schemaMapfor its own property schemas. - Registers itself in the module-level
schemaRegistry(keyed by custom element name) for cross-element$refresolution.
FASTElementDefinition.schema is optional. declarativeTemplate() assigns it
automatically after parsing; manual schema users can pass schema in the
definition object.
`ObserverMap` — automatic observable setup
An optional layer that uses the Schema to automatically:
- Call
Observable.defineProperty()for every root property on the element prototype. - Install property-change handlers that wrap newly assigned objects/arrays in
Proxyinstances. - Propagate deep property mutations back through FAST's observable system so bindings re-render.
Enabled via the observerMap() definition extension exported from@microsoft/fast-element/observer-map.js. Calling observerMap() without
arguments observes all root properties discovered in the template or supplied
schema.
Path-level observation control
The ObserverMapConfig interface accepts an optional schema for
non-declarative/manual schema use and an optional properties key that maps
root property names to a recursive path tree controlling observation
granularity:
MyElement.define(
{
name: "my-element",
template: declarativeTemplate(),
},
[
observerMap({
properties: {
user: {
name: true, // user.name — observed
details: {
age: true, // user.details.age — observed
history: false, // user.details.history — NOT observed
},
},
// root properties not listed here are skipped
},
}),
],
);Each path entry can be:
true— observe this path and all descendants (unless overridden deeper).false— skip this path and all descendants (unless overridden deeper).ObserverMapPathNode— an object with an optional$observeboolean and child property overrides, allowing alternating opt-in/opt-out to arbitrary depth.
When properties is omitted, all root properties are observed. Whenproperties is present but empty ({ properties: {} }), no root properties
are observed.
Observer-map-managed array updates replace the array reference when deepMerge
receives a new array. This avoids mutating an existing observed array while it
may be notifying subscribers and lets repeat bindings observe the new array
reference. Replaced arrays are wrapped with the schema for the assigned
property, and array observer subscriptions are installed once per array so
reprocessing does not duplicate accessors or notification work.
For non-declarative elements, pass a schema directly to observerMap():
import { FASTElement, Schema } from "@microsoft/fast-element";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
class MyElement extends FASTElement {}
const schema = new Schema("my-element");
schema.addPath({
rootPropertyName: "user",
pathConfig: {
type: "default",
parentContext: null,
currentContext: null,
path: "user.name",
},
childrenMap: null,
});
MyElement.define({ name: "my-element" }, [observerMap({ schema })]);The resolution algorithm walks the schema and configuration tree in parallel:
- If
propertiesis present and a root property is not listed, it is skipped. true/falsebooleans apply to the entire subtree.$observeon a node object controls the current level; children inherit when unspecified.- Paths in the config but not in the schema are silently ignored (forward-compatible).
`AttributeMap` — automatic `@attr` definitions
An optional layer that uses the Schema to automatically register @attr-style reactive properties for every leaf binding in the template — i.e. simple expressions like {{foo}} or id="{{foo-bar}}" that have no nested properties, no explicit type, and no child element references.
- By default (
attribute-name-strategy: "camelCase"), the binding key is treated as a camelCase property name and the HTML attribute name is derived by converting it to kebab-case (e.g.{{fooBar}}→ propertyfooBar, attributefoo-bar). This matches the build-time--attribute-name-strategyoption in@microsoft/fast-build. - When
attribute-name-strategyis"none", the attribute name and property name are both the binding key exactly as written in the template (e.g.{{foo-bar}}→ attributefoo-bar, propertyfoo-bar). No normalization is applied. - Because HTML attributes are case-insensitive, binding keys should use lowercase names (optionally dash-separated) when using the
"none"strategy. - Properties already decorated with
@attror@observableare left untouched. FASTElementDefinition.attributeLookupis keyed by the HTML attribute name, andpropertyLookupis keyed by the JS property name soattributeChangedCallbackcorrectly delegates to the newAttributeDefinition.
Enabled via the attributeMap() definition extension exported from@microsoft/fast-element/attribute-map.js. Calling attributeMap() without
arguments uses the default "camelCase" strategy. To preserve binding keys
exactly as written, pass attributeMap({ "attribute-name-strategy": "none" }).
Outside declarative templates, attributeMap() uses the optional schema on the
FAST element definition.
Syntax constants (`syntax.ts`)
All delimiters used by the parser are defined in a single Syntax interface and exported as named constants from syntax.ts. This makes the syntax pluggable and easy to audit.
| Constant | Value | Use |
|---|---|---|
openExpression / closeExpression |
{{ / }} |
Default (SSR-compatible) binding |
unescapedOpenExpression / unescapedCloseExpression |
{{{ / }}} |
Raw HTML binding |
clientSideOpenExpression / clientSideCloseExpression |
{ / } |
Client-only (event / attribute directive) binding |
repeatDirectiveOpen / repeatDirectiveClose |
<f-repeat / </f-repeat> |
Repeat directive |
whenDirectiveOpen / whenDirectiveClose |
<f-when / </f-when> |
When directive |
attributeDirectivePrefix |
f- |
Attribute directive prefix |
eventArgAccessor |
$e |
DOM event argument |
executionContextAccessor |
$c |
Execution context argument |
Package Structure
packages/fast-element/
├── src/
│ ├── components/
│ │ ├── schema.ts # Shared Schema class + schemaRegistry
│ │ └── definition-schema-transforms.ts # Definition-scoped schema transform storage
│ ├── declarative/
│ ├── index.ts # Public declarative entrypoint implementation
│ ├── interfaces.ts # Message enum (error codes)
│ ├── debug.ts # Human-readable declarative debug messages
│ ├── template.ts # declarativeTemplate(), internal <f-template> publisher, lifecycle orchestration
│ ├── template-bridge.ts # Registry/name bridge between definitions and publishers
│ ├── template-parser.ts # TemplateParser — converts declarative HTML to ViewTemplate strings/values
│ ├── observer-map.ts # observerMap() implementation
│ ├── attribute-map.ts # attributeMap() implementation
│ ├── observer-map-utilities.ts # Shared observer-map helpers
│ ├── utilities.ts # Declarative parsing helpers
│ └── syntax.ts # Syntax delimiter constants
│ └── hydration/
│ ├── markers.ts # Default 3.x marker reader and marker strategy contract
│ └── markers-v2.ts # Opt-in 2.x indexed marker reader
├── scripts/
│ └── declarative/ # Fixture build + webui integration scripts
└── test/
└── declarative/fixtures/ # One directory per feature, each with spec + index.html + main.tsModule dependency direction
The default declarativeTemplate() path avoids importing optional map
implementations. Map helpers attach schema transforms to the definition only
when the consumer passes them as define extensions.
declarative/template.ts ──imports──▶ components/definition-schema-transforms.ts (schema transform reads)
declarative/template.ts ──imports──▶ components/schema.ts (Schema)
declarative/attribute-map.ts ──imports──▶ components/definition-schema-transforms.ts (register attribute-map transform)
declarative/observer-map.ts ──imports──▶ components/definition-schema-transforms.ts (register observer-map transform)
declarative/observer-map.ts ──imports──▶ components/schema.ts (Schema types)
declarative/attribute-map.ts ──imports──▶ components/schema.ts (Schema types)
declarative/utilities.ts ──imports──▶ components/schema.ts (schemaRegistry for cross-element $ref resolution)Schema transforms run in deterministic order. attributeMap() runs beforeobserverMap(), so generated attributes are available to observer mapping.
Exports and Public API
import {
declarativeTemplate,
TemplateParser,
type ResolvedStringsAndValues,
} from "@microsoft/fast-element/declarative.js";
import {
Schema,
schemaRegistry,
type CachedPathMap,
type JSONSchema,
} from "@microsoft/fast-element/schema.js";
import { type FASTElementExtension } from "@microsoft/fast-element";
import {
attributeMap,
type AttributeMapConfig,
} from "@microsoft/fast-element/attribute-map.js";
import {
observerMap,
type ObserverMapConfig,
type ObserverMapPathEntry,
type ObserverMapPathNode,
} from "@microsoft/fast-element/observer-map.js";Declarative template helpers, map helpers, and their configuration types are
available from focused path exports.
Primary declarative exports intended for application code:
| Export | Purpose |
|---|---|
declarativeTemplate() |
@microsoft/fast-element/declarative.js template resolver for subclass define() calls; auto-defines the internal <f-template> publisher and waits for the matching template. |
TemplateParser |
@microsoft/fast-element/declarative.js standalone parser that converts declarative HTML into ViewTemplate strings/values. Can be used independently of <f-template> for programmatic template compilation. |
Schema |
@microsoft/fast-element/schema.js JSON schema builder that records binding paths discovered during template parsing. Each instance owns its own schema map and registers itself in the schemaRegistry for cross-element $ref resolution. |
schemaRegistry |
@microsoft/fast-element/schema.js module-level Map<string, Map<string, JSONSchema>> that indexes schemas by custom element name. Used for cross-element lookups (e.g. nested component $ref resolution). |
Hydration marker selection is available from@microsoft/fast-element/hydration.js. FAST Element 3.x data-free markers are
the default; enableHydration({ markers: markers_v2 }) opts into the FAST Element 2.x
indexed marker reader for staged server/client migrations.
Primary map extension exports:
| Export | Import path | Purpose |
|---|---|---|
attributeMap() |
@microsoft/fast-element/attribute-map.js |
Define extension that registers @attr-style properties for leaf bindings discovered during parsing or supplied by definition.schema. |
observerMap() |
@microsoft/fast-element/observer-map.js |
Define extension that defines observable root properties and proxy-based deep change tracking from config.schema, definition.schema, or a declarative template schema. |
The implementation element class (<f-template>), TemplateElement.config(),TemplateElement.options(), ElementOptions*, andHydrationLifecycleCallbacks are not exported from the public declarative
entrypoint. Use declarativeTemplate(), attributeMap(), observerMap(), andenableHydration() instead. The AttributeMap and ObserverMap implementation
classes are available for advanced scenarios, but application code should
normally use the extension factories.
Additionally, @microsoft/fast-element exports these types:
| Type | Source Module | Purpose |
|---|---|---|
FASTElementExtension |
fast-definitions.ts |
Definition extension callback signature used by attributeMap() and observerMap(). |
JSONSchema |
components/schema.ts |
JSON Schema interface used by Schema for property structure. |
CachedPathMap |
components/schema.ts |
Map<string, Map<string, JSONSchema>> — the shape of the schema registry. |
The map configuration types are exported from the same map-specific paths:
| Type | Import path | Purpose |
|---|---|---|
ObserverMapConfig |
@microsoft/fast-element/observer-map.js |
Configuration object for observerMap(); accepts optional schema and properties keys. |
ObserverMapPathEntry |
@microsoft/fast-element/observer-map.js |
boolean | ObserverMapPathNode — a node in the observation path tree. |
ObserverMapPathNode |
@microsoft/fast-element/observer-map.js |
Object node with optional $observe and child property overrides. |
AttributeMapConfig |
@microsoft/fast-element/attribute-map.js |
Configuration object for attributeMap(); accepts attribute-name-strategy. |
Template Syntax
The declarative syntax is a superset of HTML with three binding delimiters:
| Syntax | Example | Behaviour |
|---|---|---|
{{expr}} |
{{greeting}} |
SSR-compatible content / attribute binding |
{{{expr}}} |
{{{rawHtml}}} |
Unescaped HTML (wraps in ``) |
{expr} |
@click="{handleClick($e)}" |
Client-only binding (events, attribute directives) |
Directives
Bindings and FAST directive markup inside HTML comments are unsupported. The
string-based parsers do not reliably exclude comment contents, so commenting out
bound markup can still affect binding discovery and hydration. See the
HTML comment restrictions.
| Directive | Example |
|---|---|
<f-when value="{{expr}}"> |
Conditional rendering |
<f-repeat value="{{item in list}}"> |
List rendering |
f-slotted="{prop}" |
Slotted nodes attribute directive |
f-children="{prop}" |
Children attribute directive |
f-ref="{prop}" |
Element ref attribute directive |
For full syntax reference see syntax.md.
Data Flow
The high-level data flow from authoring to interactive component:
flowchart TD
A["Author writes declarative HTML
using f-template with binding expressions"] --> B["Server renders hydratable HTML
with fe:b comments and data-fe attributes"]
B --> C[Browser loads JS bundle]
C --> D["MyElement.define called
with template: declarativeTemplate()"]
D --> E["declarativeTemplate() defines internal f-template
in the target registry"]
E --> F["f-template connects to DOM
→ registers publisher with bridge"]
F --> G["definition requests template
via registry + name bridge"]
G --> H[transformInnerHTML normalises HTML entities]
H --> I["TemplateParser parses bindings/directives
builds Schema, strings, values"]
I --> J["Run definition schema transforms
attributeMap before observerMap"]
J --> K["ViewTemplate.create(strings, values)
returned to definition"]
K --> L["FASTElementDefinition.define
registers element with platform"]
L --> M{"enableHydration() called?"}
M -- yes --> N["ElementController hydrates existing DOM
via template.hydrate()"]
M -- no --> O["ElementController client-side renders
existing DSD is not reused"]
N --> P[Interactive component]
O --> PTemplate Parsing Pipeline
The internal <f-template> publisher orchestrates the pipeline by creating aTemplateParser instance and delegating all parsing logic to it. The recursive
parsing context is encapsulated in a TemplateResolutionContext object internal
to the parser, keeping method signatures lean.
Architecture
The parsing pipeline is split across two classes plus definition-scoped
transforms:
- Internal
<f-template>publisher (template.ts) — Custom element
lifecycle, registry/name bridge registration, template publication, and
schema-transform execution. It is a nativeHTMLElement, not aFASTElement. TemplateParser(template-parser.ts) — Synchronous template parser:
converts declarative HTML intostrings/valuesarrays forViewTemplate.create(). Uses aStringsAccumulatorto track the running
previous-string in O(1) per binding site instead of O(N)join("")calls.
Independently testable without DOM.- Schema transforms (
definition-options.ts) — Extension-provided callbacks run
after parsing and beforeViewTemplate.create().attributeMap()runs beforeobserverMap().
/* Detailed source-code truncated for AI context efficiency. */TemplateParser method decomposition
| Method | Visibility | Role |
|---|---|---|
parse() |
public | Entry point: parses declarative HTML into { strings, values }. |
createTemplate() |
public | Creates a ViewTemplate from resolved strings and values, lazily installing declarative debug messages. |
resolveStringsAndValues() |
private | Creates strings/values arrays and delegates to resolveInnerHTML(). |
resolveInnerHTML() |
private | Recursive HTML parser that dispatches to data binding or template directive handlers. |
resolveDataBinding() |
private | Thin dispatcher that routes to resolveContentBinding(), resolveAttributeBinding(), or resolveAttributeDirectiveBinding(). |
resolveContentBinding() |
private | Handles {{expression}} in text content. |
resolveAttributeBinding() |
private | Handles {{expression}} in HTML attributes; dispatches to resolveEventBinding() or resolveAccessBinding() based on aspect. |
resolveAttributeDirectiveBinding() |
private | Handles f-children, f-slotted, f-ref directives. |
resolveAccessBinding() |
private | Shared helper for access-type bindings (content, boolean-attribute fallback, default attribute). |
resolveEventBinding() |
private | Handles event bindings (@event), including arg parsing and owner resolution. |
resolveTemplateDirective() |
private | Handles <f-when> and <f-repeat> directives. |
resolveAttributeDirective() |
private | Creates FAST children(), slotted(), or ref() directives. |
TemplateResolutionContext
The TemplateResolutionContext interface (internal to TemplateParser) groups the stable fields that flow through the recursive parsing pipeline:
interface TemplateResolutionContext {
parentContext: string | null; // Current repeat item alias (e.g. "item")
level: number; // Nesting depth for repeat directives
schema: Schema; // JSON schema builder for property tracking
}rootPropertyName is intentionally kept separate because it is selectively mutated per branch and must not leak across sibling binding resolutions.
Key parsing functions (utilities.ts)
| Function | Role |
|---|---|
getNextBehavior(innerHTML) |
Top-level scanner: returns the next DataBindingBehaviorConfig or TemplateDirectiveBehaviorConfig, or null when done. |
getNextDataBindingBehavior(innerHTML) |
Identifies whether the next binding is {{}}, {{{}}}, or {} and classifies it as content/attribute/attributeDirective. |
getNextDirectiveBehavior(innerHTML) |
Finds the next <f-when> or <f-repeat> and its matching close tag. |
bindingResolver(...) |
Builds a (x, c) => value closure for a given path; also calls schema.addPath(). |
pathResolver(path, contextPath, level, schema) |
Returns a closure that traverses an object using dot-notation, handling repeat context levels. |
getBooleanBinding(...) |
Returns a (x, c) => boolean closure for <f-when> expressions. |
assignObservables(schema, rootSchema, data, target, rootProperty) |
Wraps objects/arrays in Proxy for deep observation. |
deepMerge(target, source) |
Merges source into an existing proxy, preserving proxy identity and triggering observable notifications. |
transformInnerHTML(html) |
Normalises HTML-encoded operator characters (>, <, etc.) used in <f-when> expressions. |
escapeBracesInCodeElements(html) |
Replaces { / } characters inside every <code> element with { / } so binding-like syntax in code samples renders literally rather than being interpreted as FAST bindings. Client-side half of a two-stage escape — the brace escape mirrors the server-side pass in microsoft-fast-build (necessary client-side because { / } decode back to literal { / } in the DOM and .innerHTML does not re-encode them). The angle-bracket escape for FAST directive tags (<f-when>, </f-when>, <f-repeat>, </f-repeat>) inside <code> runs only on the server because the DOM serializer re-encodes < / > in text content automatically. Real HTML elements (``) and custom elements inside <code> keep their angle brackets and continue to render as live DOM elements. Modeled on Microsoft WebUI's webui-press markdown renderer. |
Binding classification
innerHTML token
├── {{{ ... }}} → unescaped content binding (innerHTML div)
├── {{ ... }} → default binding
│ ├── attr="{{expr}}" → attribute binding (aspect: null / ":" / "?")
│ └── {{expr}} in text → content binding
└── { ... } → client-side binding
├── @event="{handler(...)}" → event binding (aspect "@")
└── f-dir="{prop}" → attribute directive bindingSchema and Observer Map
The Schema class accumulates all binding paths discovered during parsing into an instance-level JSON Schema map (schemaMap) indexed by rootPropertyName → JSONSchema. Each Schema instance also registers itself in the module-level schemaRegistry (keyed by custom element name) for cross-element $ref resolution.
flowchart LR
A["Template binding\nuser.details.age"] --> B[bindingResolver]
B --> C[schema.addPath\ntype:'access'\npath:'user.details.age'\nrootProperty:'user']
C --> D["schema.schemaMap\n{'user' => JSONSchema}"]
C --> D2["schemaRegistry\n{'my-el' => schemaMap}"]
D --> E[ObserverMap.defineProperties]
E --> E1["applyConfigToSchema\nstamps $observe: false on excluded schema nodes"]
E1 --> F[Observable.defineProperty on prototype\nfor 'user']
E1 --> G[install instanceResolverChanged handler]
G --> H["when user= is set:\nassignObservables checks schema.$observe\n→ Proxy wraps observed paths only\ndeep mutations → Observable.notify"]For a deep dive into the schema structure, context tracking, and proxy system
see
schema-observer-map.md.
`$observe` flag on schema nodes
When an ObserverMapConfig with a properties key is provided, ObserverMap.defineProperties() calls applyConfigToSchema() to stamp $observe: false on excluded schema nodes before the proxy system runs. This is a one-time pre-processing pass that walks the config and schema trees in parallel:
falsein the config →$observe: falseis stamped recursively on the node and all its descendants.$observe: falseon a config node → the schema node is stamped, and unlisted children inherit the stamp.truein the config → no stamp needed (observed is the default).- Config paths not in the schema are silently ignored.
Convention: stamp-only-when-excluding. The $observe flag is only ever set to false — it is never explicitly set to true. Absence of $observe (i.e. undefined) means the node is observed. This means:
- When Observer Map is enabled without a
propertiestree,applyConfigToSchema
is never called and no schema nodes are mutated — zero overhead for the
common case. - The proxy system uses
isSchemaExcluded(schema)(checks$observe === falsewith no observed descendants) as the single predicate for all skip/suppress decisions. - Schema nodes without
$observeare always treated as observed.
AttributeMap and leaf bindings
When attributeMap() is enabled with a declarative template, it registers a
definition schema transform. With a manually supplied definition schema, it runs
immediately. The extension constructs an AttributeMap implementation and callsdefineProperties(). It iterates Schema.getRootProperties() and skips any
property whose schema entry contains properties, type, or anyOf — keeping
only plain leaf bindings. For each leaf:
- The schema key is used as the JS property name.
- The HTML attribute name depends on the
attribute-name-strategy:"camelCase"(default): the attribute name is derived by converting the camelCase property name to kebab-case (e.g.fooBar→foo-bar)."none": the attribute name equals the property name (e.g.foo-bar→foo-bar).
- A new
AttributeDefinitionis registered viaObservable.defineProperty. FASTElementDefinition.attributeLookupis keyed by the HTML attribute name andpropertyLookupis keyed by the JS property name soattributeChangedCallbackcan route attribute changes to the correct property.
When using the "none" strategy, property names may contain dashes and must be
accessed via bracket notation (e.g. element["foo-bar"]). When using"camelCase", property names are standard JS identifiers (e.g.element.fooBar).
Schema transforms run in deterministic order. When both extensions are supplied,
attribute mapping runs before observer mapping.
Lifecycle
sequenceDiagram
participant App as Application JS
participant FER as FASTElementDefinition
participant FTE as internal f-template
participant EC as ElementController
participant Hydration as enableHydration().whenHydrated()
App->>Hydration: const hydration = enableHydration() [optional]
App->>FER: MyElement.define({name:'my-el', template: declarativeTemplate()}, [attributeMap(), observerMap()])
note over FER: definition composed; resolver waits for template
DOM->>FTE: f-template connected to DOM
FTE->>FTE: bridge matches registry + name
FTE->>FTE: parse template → schema → transforms → ViewTemplate
FTE->>FER: return viewTemplate to resolver
FER->>FER: customElements.define('my-el', MyElement)
DOM->>EC: element instance connects with existing shadow root
EC->>EC: isPrerendered = true (existing shadow root detected)
alt enableHydration() was called
EC->>EC: template.hydrate() — maps existing DOM to binding targets
EC->>Hydration: resolve active batch when all pending elements finish
else hydration not enabled
EC->>EC: client-side render; isHydrated = false
endPromise readiness reference
| Promise | Import path | Resolves when |
|---|---|---|
enableHydration().whenHydrated("my-element") |
@microsoft/fast-element/hydration.js |
Hydration work for that tag name completes. |
enableHydration().whenHydrated() |
@microsoft/fast-element/hydration.js |
The active hydration batch completes. |
By default, hydration no-ops for later prerendered batches after the initial
batch completes. SetenableHydration({ stopHydration: StopHydration.never }) when Declarative Shadow
DOM may be streamed into the page after the initial hydration batch. In that
mode, enableHydration().whenHydrated() intentionally remains pending because
there is no global completion point.
For usage examples see
lifecycle.md.
Integration with fast-element
The declarative runtime is a thin orchestration layer on top of@microsoft/fast-element. It does not re-implement any reactive primitives; it
converts declarative HTML syntax into the same data structures that html
tagged templates produce.
| fast-element primitive | How the declarative runtime uses it |
|---|---|
FASTElement |
Base class for user components; the internal <f-template> publisher is a native HTMLElement |
FASTElementDefinition.register() / template resolvers |
Deferred element registration — element waits for its template |
FASTElementDefinition.schema |
Optional schema used by schema-driven extensions; assigned automatically by declarativeTemplate() and available for manual schemas |
FASTElementExtension |
Extension callback mechanism used by attributeMap() and observerMap() to attach schema transforms before template resolution or consume manually supplied schemas |
ViewTemplate.create(strings, values) |
Compiles the resolved strings/values arrays into a ViewTemplate |
ElementController |
Automatically detects prerendered content (isPrerendered) and hydrates server-rendered DOM using fe-b comment/dataset markers via template.hydrate() |
Observable.defineProperty() |
Defines observable root properties on element prototypes (ObserverMap) |
Observable.getNotifier() |
Triggers change notifications from proxy handlers |
when(expr, template) |
FAST directive used for <f-when> |
repeat(expr, template) |
FAST directive used for <f-repeat> |
slotted(options) |
FAST directive used for f-slotted |
children(prop) |
FAST directive used for f-children |
ref(prop) |
FAST directive used for f-ref |
Deferred template attachment via define
Standard subclass define() calls return a Promise that resolves immediately when a concrete template is provided at definition time. When template: declarativeTemplate() is used, the Promise resolves after the matching <f-template> supplies a concrete template through the bridge. This unified API replaces the previous defineAsync() / composeAsync() methods.
Hydration Model
When declarative templates are used, the server must render:
- The custom element tag with its attributes and initial state.
- A
<template shadowrootmode="open">containing pre-rendered HTML annotated with FAST's hydration markers. - An
<f-template>element somewhere in the page that carries the template definition.
With declarativeTemplate(), connection gating happens before platform registration: the resolver waits for the matching <f-template> and keeps the definition concrete before elements can connect. Hydration can therefore start immediately when ElementController.connect() runs. The defer-hydration and needs-hydration attributes are no longer needed in server-rendered markup.
Hydration marker formats
enableHydration() uses the FAST Element 3.x data-free marker reader by
default. The marker strategy is shared by target discovery and nested repeat
hydration so a view uses one consistent format throughout its hydration walk.
The optional markers_v2 strategy contains the legacy indexed-marker parsing logic and
is selected with enableHydration({ markers: markers_v2 }).
Content bindings use HTML comments (data-free, matched by string equality):
Attribute bindings use a single data-fe dataset attribute with binding count:
<el data-fe="3">Repeat directives wrap each item in comment pairs:
...item DOM...For detailed examples see
rendering.md.
Testing Architecture
Each feature is verified by a Playwright integration test against a live
Vite dev server. The test/declarative/fixtures/ directory contains one
subdirectory per feature:
test/declarative/fixtures/<feature>/
├── <feature>.spec.ts # Playwright test
├── entry.html # Entry template with root custom elements
├── fast-build.config.json # Build configuration for @microsoft/fast-build
├── index.html # Pre-rendered page (GENERATED by scripts/declarative/build-fixtures.js — do not edit)
├── main.ts # Component definitions, declarativeTemplate(), extensions, and enableHydration() setup
├── state.json # Initial state for server-side rendering
└── templates.html # Declarative <f-template> definitionsFixtures are auto-discovered by scanning for directories that contain entry.html, templates.html, state.json, and fast-build.config.json. Both the build script and the Vite config pick up new fixtures automatically — no registration step is needed.
For fixtures that use SSR-style pre-rendered HTML,scripts/declarative/build-fixtures.js invokes @microsoft/fast-build with--config pointing to each fixture's fast-build.config.json to generateindex.html from the configured source files.
WebUI Integration Tests
A separate integration test suite validates that @microsoft/webui can build and render the same fixture templates that @microsoft/fast-build processes. This is split into two steps:
- Build (
npm run build:fixtures:webui) — runsscripts/declarative/build-fixtures-with-webui.js, which extracts<f-template>elements, converts each component template withfast convert --syntax=webui-prerelease, builds each fixture withwebui build --plugin=fast, renders the protocol withstate.json, and
writes the output alongsidemain.tsand assets totemp/integrations/webui/fixtures/. - Test (
npm run test:webui-integration) — builds the fixtures, then runs
the same Playwright specs against the webui-rendered output served by a Vite
dev server on port 5174 (configured inplaywright.declarative.webui.config.ts).
Fixtures that intentionally contain invalid FAST declarative templates are skipped in
the WebUI build because fast convert validates and rejects them before WebUI
renders. Run locally with npm run test:webui-integration or via theci-webui-integration.yml GitHub Action on PRs and pushes to main.
Skipped tests
Some tests are conditionally skipped when running under the webui integration
config. The playwright.declarative.webui.config.ts file setsprocess.env.FAST_WEBUI_INTEGRATION = "true", and individual tests check this
variable with test.skip() to opt out of cases that exercise known differences
between fast-build and webui rendering:
errors.spec.ts— "throws an error when no template element is present": webui does not render<f-template>elements that lack a<template>child, so the expected error is never thrown.
Hydration readiness
Fixtures that exercise prerendered output wait for hydration to complete before
running assertions. Each main.ts calls enableHydration() and then awaits
the returned controller's whenHydrated() promise to set a global flag, and each spec file callspage.waitForFunction() after page.goto() to block until the flag is set.
See test/declarative/fixtures/README.md
for the implementation pattern.
See
test/declarative/fixtures/WRITING_FIXTURES.md
for the complete fixture authoring guide,
test/declarative/fixtures/README.md
for a quick reference, and
test/declarative/fixtures/extensions/observer-map-deep-merge/README.md
for an example of a complex multi-feature fixture.
Further Reading
| Document | Topic |
|---|---|
| syntax.md | Installation, syntax reference, hydration promises, usage examples |
| rendering.md | Hydratable HTML format: comment markers, dataset attributes, directive markers |
| lifecycle.md | Phase-by-phase rendering lifecycle, hydration readiness, performance notes |
| schema-observer-map.md | Deep dive into Schema JSON structure, ObserverMap proxy system, debugging |
| test/declarative/fixtures/README.md | Quick reference for fixture structure |
| test/declarative/fixtures/WRITING_FIXTURES.md | Complete guide to writing new Playwright fixture tests |
| test/declarative/fixtures/extensions/observer-map-deep-merge/README.md | Complex deep-merge fixture: observable arrays, nested repeats, conditionals |
Packages Fast Element Docs Declarative Lifecycle (`packages/fast-element/docs/declarative/lifecycle.md`)
Rendering Lifecycle
This document describes the declarative rendering lifecycle inside@microsoft/fast-element.
Overview
The FAST Element rendering lifecycle involves a coordinated process between the
core runtime and its declarative entrypoint:
@microsoft/fast-element: Provides the coreFASTElementbase class and
element definition system.@microsoft/fast-element/declarative.js: Provides thef-template
custom element that processes HTML templates and attaches them to FAST
elements as aViewTemplatein lieu of anhtmltemplate created during
a subclassdefine()call. The preferred path usesdeclarativeTemplate()so
the subclassdefine()call waits for the matching declarative template and keeps
the definition concrete before registration completes.
Lifecycle Phases
Given a DOM which includes an f-template and a component:
<my-component text="Hello World">
<template shadowrootmode="open">
<h1>Hello World</h1>
</template>
</my-component>
<f-template name="my-component">
<template>
<h1>{{text}}</h1>
</template>
</f-template>The following phases will then be kicked off once the JavaScript is parsed.
Phase 1: Definition Resolution
Custom elements begin their lifecycle by composing a definition that points atdeclarativeTemplate(). The resolver waits for a matching declarative template
and returns a concrete ViewTemplate before the platform registration step.
// Custom element class definition
class MyComponent extends FASTElement {
@attr text: string = "";
}
// Register with the declarative template bridge
MyComponent.define({
name: "my-component",
template: declarativeTemplate(),
});Key characteristics of this phase:
- The element definition stays unresolved until a matching declarative template is available
- The resolved template is concrete before platform registration completes
Phase 2: Declarative Template Bridge
declarativeTemplate() from @microsoft/fast-element/declarative.js
automatically ensures that f-template is defined in the same registry as the
FAST element being composed.
Phase 3: Template Processing and Attachment
When an f-template element is connected to the DOM, it initiates the template attachment process.
The lifecycle flow during this phase:
- Template Discovery: The resolver waits for a matching
<f-template name="...">in the same registry as the element definition. - Template Element Connection: The matching
f-templateelement'sconnectedCallback()registers it with the declarative template bridge. - Template Processing: The bridge reads and transforms the markup, builds
the schema, appliesobserverMap()/attributeMap()behavior, and resolves
data bindings, directives, and other template features into theViewTemplate
model which is also used by the@microsoft/fast-elementhtmltag template. - Template Attachment: The concrete
ViewTemplateis returned to
the subclassdefine()call, which assigns it to the definition before platform
registration completes.
Phase 4: Composition Completion
Once the template is attached to the partial definition, the element completes its composition:
compose()Execution: The element definition internally completes its composition process- Platform Registration: The completed element definition is fully registered with the platform's custom element registry
Phase 5: Element Instantiation and Hydration
When custom elements are instantiated in the DOM, the following occurs:
- Element Creation: The platform creates instances of the custom element
- Prerendered Content Detection:
ElementControllerdetects the existing shadow root from SSR —isPrerenderedresolvestrue - Hydration Check: If
enableHydration()was called and the template is hydratable, the element hydrates —isHydratedresolvestrue. Otherwise it falls back to client-side rendering. - Concrete Template Ready: Because
declarativeTemplate()resolved during
definition,connect()starts with the final template already attached. - Hydration:
ElementControllerusestemplate.hydrate()to create aHydrationViewthat maps existing DOM nodes to binding targets usingfe:b
/fe:/bmarkers
The DOM after hydration should look like this:
<my-component text="Hello World">
<template shadowrootmode="open">
<h1>Hello World</h1>
</template>
</my-component>Key Integration Points
Fast Element Registry
The fastElementRegistry serves as the central coordination point between the
two packages and is available to consumers from@microsoft/fast-element/registry.js:
- Stores partial element definitions created by
define() - Provides lookup mechanism via
register()for template attachment - Resolves
fastElementRegistry.whenRegistered(tagName)when a matching
definition is registered - Maintains the registry of all FAST element definitions
Observable Pattern
Both packages use the Observable pattern for coordination:
- Element definition template assignment notifies observers that are waiting for a concrete template
- Template attachment triggers observable notifications to complete the lifecycle
Error Handling
The lifecycle includes error handling for missing components:
- Template elements throw errors if no corresponding element definition is found
- Element definitions can exist without templates (partial state)
Performance Considerations
The asynchronous nature of the lifecycle provides several performance benefits:
- Progressive Enhancement: Elements can be registered before templates are loaded
- Code Splitting: Templates can be loaded separately from element definitions
- Reduced Blocking: Template processing doesn't block element registration
- Hydration Optimization: Server-side rendered content can be hydrated efficiently
This coordinated lifecycle enables powerful scenarios like server-side rendering, progressive enhancement, and dynamic template loading while maintaining the reactive capabilities of FAST Element.
Hydration promise APIs
FAST Element exposes promises for hydration readiness points that are useful to
application code.
Hydration must be explicitly opted into by calling enableHydration(). Await
the returned controller's whenHydrated() promise when code needs to run after
the active hydration batch completes. Pass a tag name to whenHydrated(tagName)
when code needs to wait for a specific FAST element.
import { enableHydration } from "@microsoft/fast-element/hydration.js";
const hydration = enableHydration();
await hydration.whenHydrated("my-component");
await hydration.whenHydrated();By default, hydration no-ops for later prerendered batches after the initial
batch completes. Set stopHydration: StopHydration.never inenableHydration() when streaming Declarative Shadow DOM should continue
hydrating after the initial batch. In that mode, hydration.whenHydrated()
intentionally remains pending because hydration has no global completion point.
Packages Fast Element Docs Declarative Rendering (`packages/fast-element/docs/declarative/rendering.md`)
Non-browser HTML rendering
This document details information on rendering hydratable content and includes some pointers on how Dependency Injection can be used to create services that hydrate a custom elements state. Non-browser rendering in this context means producing HTML that a browser can parse and hydrate later; it does not mean running the FAST Element client runtime in a worker, server-side JavaScript runtime, or other non-window host.
Initial State
To create initial state this should be added somewhere accessible by your service, in this example a <script> tag can be used to provide a global initial state.
Example:
<script>
window.__INITIAL_STATE__ = {
text: "Hello world"
}
</script>Add a Dependency Injection service in your bundle to access properties in your state.
Simple example (initial-state.ts):
import { DI } from "@microsoft/fast-element/di.js";
interface InitialState {
text: string;
}
export interface IInitialStateService {
init(): InitialState;
}
export class InitialStateService implements IInitialStateService {
private initialState: any;
constructor() {
this.initialState = window.__INITIAL_STATE__;
}
public init(...keys: Array<string>): InitialState {
const config: any = {};
for (let key of keys) {
config[key] = this.initialState[key];
}
return config;
}
}
export const initialStateFactory = DI.createContext<IInitialStateService>(
'i-initial-state-service',
(x) => x.singleton(InitialStateService)
);Include the DI service to your component.
Example:
import { attr, FASTElement, Observable } from "@microsoft/fast-element";
import {
initialStateFactory,
InitialStateService,
type IInitialStateService,
} from "./initial-state.js";
import { inject } from "@microsoft/fast-element/di.js";
export class MyComponent extends FASTElement {
@inject(initialStateFactory) initialStateService!: InitialStateService;
@attr
text: string = "";
service!: IInitialStateService;
connectedCallback() {
super.connectedCallback();
this.loadItemFromInitialState();
}
loadItemFromInitialState(): void {
const initialState = this.initialStateService.init(["text"]);
this.text = initialState.text;
}
}
MyComponent.define({
name: "my-component",
template: declarativeTemplate(),
});When the element connects, ElementController automatically detects the
existing shadow root from SSR and sets isPrerendered = true.declarativeTemplate() keeps the definition template concrete before
registration completes, so the element can hydrate the prerendered shadow root
immediately. The defer-hydration and needs-hydration attributes are no
longer needed.
Hydration Comments and Datasets
When hydrating the HTML, FAST uses ElementController which detects an
existing shadow root (from SSR or declarative shadow DOM) and setsisPrerendered = true. It then uses template.hydrate() to create aHydrationView that maps existing DOM nodes to binding targets using hydration
"markers" such as comments and dataset attributes. By default the declarative
runtime in @microsoft/fast-element assumes component hydration will occur, so
rendering hydratable markup is required.
Bindings
Content Bindings
Content binding markers are represented using HTML comments. These comments are used to indicate where dynamic content exists in the template. Markers carry no embedded data — they are fixed strings matched by string equality (not regex).
- Start binding: ``
- End binding: ``
Binding pairs are matched using balanced depth counting: each start marker increments a depth counter and each end marker decrements it. When the counter returns to zero the pair is complete.
Content Binding Marker Examples
Note
Examples shown below mostly skip the wrapping custom element and the internal template element with shadowrootmode="open".
Typically along with the content from the examples below, the rendering should include:
<my-component>
<template shadowrootmode="open" shadowroot="open">
</template>
</my-component>Simple content example
Content bindings such as:
<h1>{{text}}</h1>When combined with state such as:
{
"text": "Hello world"
}Should result in:
<h1>Hello world</h1>Attribute Bindings
Attribute bindings are tracked using a single dataset attribute: data-fe="N" where N is the number of attribute bindings on the element.
Examples
Single attribute example
Attribute binding such as:
<h1 greeting="{{greeting}}"></h1>When combined with state such as:
{
"greeting": "Hello"
}Should result in:
<h1 data-fe="1" greeting="Hello"></h1>Multiple attribute bindings
When multiple attribute bindings exist on the same element:
<h1 greeting="{{greeting}}" subtitle="{{subtitle}}" punctuation="{{punctuation}}"></h1>When combined with state such as:
{
"greeting": "Hello",
"subtitle": "world",
"punctuation": "!"
}Should result in:
<h1 data-fe="3" greeting="Hello" subtitle="world" punctuation="!"></h1>Mixed attribute and content example
Multiple attributes and content bindings such as:
<div show="{{show}}" appearance="{{appearance}}" punctuation="{{punctuation}}">
<h1>{{text}}</h1>
<span>{{subtitle}}</span>
<span>{{punctuation}}</span>
</div>Should result in:
<div data-fe="3" show appearance="large" punctuation="!">
<h1>Hello</h1>
<span>world</span>
<span>!</span>
</div>Directives
Directives are treated differently from bindings as they include a template. This means that in addition to the binding used to determine the logic for the directive FAST also requires a separate marker for the directive itself to demarkate the beginning and end of the template.
Repeat
Example repeat binding:
<f-repeat value="{{item in list}}">
<span>{{item}}</span>
</f-repeat>Combined with state:
[
"Bob",
"Alice",
"Sue"
]Should result in:
<span>
Bob
</span>
<span>
Alice
</span>
<span>
Sue
</span>Note that the repeat markers are data-free — they carry no index or ID. Pairing uses balanced depth counting. Additionally, a binding wraps the repeat markers; even if the array is empty, this binding must be rendered.
Example result of an empty array:
When
The when directive is either present in the DOM or not, and therefore does not need an extra marker for the template.
Example when binding:
<f-when value="{{show}}">
<span>{{text}}</span>
</f-when>Combined with state:
{
"show": true,
"text": "Hello world"
}Should result in:
<span>
Hello world
</span>If the when is evaluated to falsy then we can safely leave the binding markers only.
Example state:
{
"show": false,
"text": "Hello world"
}Should result in:
Host attributes from ``
Build-time renderers (e.g. @microsoft/fast-build) forward attributes declared on the inner <template> element of an <f-template> onto the rendered host element opening tag.
Example template:
<f-template name="my-button">
<template tabindex="0" ?disabled="{{isDisabled}}">
<button><slot></slot></button>
</template>
</f-template>Combined with author HTML <my-button></my-button> and state:
{
"isDisabled": true
}Should result in:
<my-button tabindex="0" disabled defer-hydration needs-hydration>
<template shadowrootmode="open" shadowroot="open">
<button><slot></slot></button>
</template>
</my-button>Rules:
- Client-only attributes (
@event,:prop,f-ref,f-slotted,f-children) are skipped — they have no meaning on a server-rendered host element. - Author attributes on the host element always win on conflicts (case-insensitive name match; the dedupe key for
?name="{{expr}}"is the barename). - Static
name="value"is forwarded verbatim (HTML-escaped); static booleannameis emitted bare. name="{{expr}}"is resolved against the element's initial child state; primitives are emitted asname="value",Array/Object/Nulland missing values are stripped.?name="{{expr}}"is evaluated as a boolean against the same state; the barename(without?) is emitted when truthy and skipped when falsy.
Client Side Bindings
Client side bindings are bindings which the client needs, but they are not necessary as part of an initial render. These must still be accounted for when creating hydration comments however, as the template needs to know which elements to attach these bindings to. There are two types of client side bindings, events and attribute directives. These do not require state as they are bound to class methods or properties.
Event Bindings
Event bindings such as @keydown and @click can be represented with hydration comments.
Example event binding:
<button @click="{handleClick($e)}">Button</button>Should result in:
<button data-fe="1">Button</button>Attribute Directives
Attribute directives such as f-slotted and f-ref can be represented with hydration comments.
Example f-ref binding:
<button f-ref="{button}">Button</button>Should result in:
<button data-fe="1">Button</button>More Examples
Nested Whens
Example when binding:
<f-when value="{{show}}">
<span>{{text}}</span>
<f-when value="{{showInternal}}">
<span>{{internalText}}</span>
</f-when>
</f-when>Combined with state:
{
"show": true,
"text": "Hello world",
"showInternal": true,
"internalText": "Hello pluto"
}Should result in:
<span>
Hello world
</span>
<span>
Hello pluto
</span>Nested Repeats
Example when binding:
<f-repeat value="{{item in items}}">
<div>
<span>{{item.name}}</span>
<f-when value="{{!!item.nested}}">
<ul>
<f-repeat value="{{person in item.nested}}">
<li>{{person.name}}</li>
</f-repeat>
</ul>
</f-when>
</div>
</f-repeat>Combined with state:
{
"items": [
{
"name": "Bob"
},
{
"name": "Alice"
},
{
"name": "Sue",
"nested": [
{
"name": "Amy"
},
{
"name": "Clarice"
},
{
"name": "Lawrence"
}
]
}
]
}Should result in:
<div>
<span>Bob</span>
</div>
<div>
<span>Alice</span>
</div>
<div>
<span>Sue</span>
<ul>
<li>
Amy
</li>
<li>
Clarice
</li>
<li>
Lawrence
</li>
</ul>
</div>Nested components with ``
This example shows the wrapping custom element tag as well as the template component with shadowrootmode="open" for the sake of illustrating an example that would exist in the DOM.
Example template of component "nested-component":
<f-when value="{{showButton}}">
<my-button appearance="{{appearance}}">{{text}}</my-button>
</f-when>Example template of component "my-button":
<button class="{{appearance}}">
<slot></slot>
</button>Combined with state:
{
"showButton": true,
"text": "Hello world",
"appearance": "fancy"
}Should result in:
<nested-components>
<template shadowrootmode="open" shadowroot="open">
<my-button data-fe="1" appearance="fancy">
<template shadowrootmode="open" shadowroot="open">
<button class="default" data-fe="1">
<slot></slot>
</button>
</template>
Hello world
</my-button>
</template>
</nested-components>Rendering Lifecycle
For information about the declarative rendering lifecycle inside@microsoft/fast-element, see
lifecycle.md.
Packages Fast Element Docs Declarative Schema Observer Map (`packages/fast-element/docs/declarative/schema-observer-map.md`)
Schema and Observer Map Architecture
This document provides a technical explainer for how the Schema andObserverMap classes work together to describe data objects and automatically
observe them in FAST elements. Declarative <f-template> markup creates schemas
automatically, but the schema and observer map implementation are factored so
they can also be used without declarative templating.
Table of Contents
- Overview
- The Schema Class
- The Observer Map Class
- Integration with f-template
- Initial Path Processing Flow
- Dynamic Path Processing Updates
- Observable Proxy System
- Examples
- Technical Details
Overview
The Schema and Observer Map architecture enables automatic observation of complex nested data objects in FAST HTML templates. This system solves the problem of manually tracking all the binding paths used in declarative HTML templates, automatically creating observables for nested properties that are referenced in the template bindings.
Important: This system is designed specifically for observable data objects (objects, arrays, and primitives). It explicitly does not handle binding paths that are assumed to be functions, methods, or other non-data constructs like event handlers.
Key Components
- Schema Class: Generates JSON schemas that describe the structure and binding paths of data objects based on template analysis or manually registered paths
- Observer Map Class: Uses the schema information to automatically define observable properties and create proxies for nested object observation
- f-template Integration: Template processing automatically populates schemas and configures observer maps during template compilation
- Map helpers:
@microsoft/fast-elementexposes the map helpers independently from declarative templating
Supported Data Types
The system automatically handles:
- Objects: Nested object structures with observable properties
- Arrays: Arrays with observable mutations and nested object elements
- Primitives: String, number, boolean values as object properties
Explicitly Not Handled
The system does not process:
- Function bindings: Event handlers and method calls like
@click="{handleClick()}" - Context paths: Bindings using the
$cprefix (e.g.,$c.parent.showNames) resolve directly from theExecutionContextand are not part of the data model - Computed expressions: Complex expressions that evaluate to non-data values
- Non-observable constructs: Any binding assumed to reference functions or non-data objects
The Schema Class
The Schema class is responsible for building JSON Schema definitions that map the structure of data objects used in template bindings. It analyzes binding paths from template processing and constructs schemas that describe:
- Root properties and their nested structures
- Array contexts for repeat operations
- Object property relationships
- Cross-references to child components
Important: Each schema created corresponds to a property that will belong to the custom element's class. The schema describes the expected structure and relationships of data that will be assigned to that property, enabling automatic observation of nested objects and arrays within that property's data structure.
Core Methods
Constructor
constructor(name: string)Creates a new schema instance for a specific custom element name and initializes an instance-level schemaMap. The instance also registers itself in the module-level schemaRegistry for cross-element $ref resolution. FASTElementDefinition.schema is optional; declarative templates assign it after parsing, while manual schema users can pass one in the element definition or directly to observerMap({ schema }).
addPath
public addPath(config: RegisterPathConfig)Registers a binding path discovered during template processing. The configuration includes:
pathConfig: Describes the path type (access,default,repeat,event)rootPropertyName: The root property this path belongs tochildrenMap: Information about child components referenced in this path
Note: Only access, default, and repeat path types result in schema entries. Event paths are identified but not processed since they represent function calls rather than observable data.
getSchema
public getSchema(rootPropertyName: string): JSONSchema | nullRetrieves the JSON schema for a specific root property.
Path Types
The schema handles different types of binding paths:
access/default: Direct property access like{{user.name}}repeat: Array iteration contexts like{{item}}within<f-repeat>event: Event handler bindings like@click="{handleClick()}"- Note: These are explicitly not processed by the schema system as they are assumed to be functions rather than observable data objects
Schema Structure
Generated schemas follow JSON Schema format with FAST-specific extensions:
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://fast.design/schemas/my-element/users.json",
"type": "array",
"$ref": "#/$defs/user",
"$defs": {
"user": {
"$fast_context": "users",
"$fast_parent_contexts": [null],
"type": "object",
"properties": {
"name": {},
"details": {
"type": "object",
"properties": {
"age": {}
}
}
}
}
}
}Note on Property Types: Properties accessed through binding paths (like name and age above) are defined with empty objects {} rather than specific JSON Schema types. This is because it's not possible to determine the exact primitive type (string, number, boolean, etc.) from the path syntax alone. The system focuses on structural relationships rather than specific data types for leaf properties.
The Observer Map Class
The ObserverMap class uses schema information to automatically configure observable properties on custom element prototypes. It creates the infrastructure needed to observe nested object changes without manual setup.
Constructor
constructor(classPrototype: any, schema: Schema, config?: ObserverMapConfig)Creates an observer map instance that will configure the provided class prototype using the schema information.
defineProperties
public defineProperties(): voidThe main method that:
- Iterates through all root properties defined in the schema (each schema instance contains multiple root property schemas)
- Defines observable properties using FAST Element's
Observable.defineProperty(an alternative to the@observabledecorator syntax used in custom element classes) - Sets up property change handlers that create proxies for nested objects
Property Change Handling
When a root property transitions from undefined to a defined value, the observer map:
- Analyzes the schema to understand the expected object structure
- Creates proxies using the
assignObservablesutility function - Establishes deep observation of nested properties based on the schema of that root property
Integration with f-template and manual schemas
The Schema and Observer Map classes integrate seamlessly with the f-template system, but they are not tied to it:
Template Processing Flow
- Template Parsing: When an
f-templateelement is processed, it creates aSchemainstance - Binding Discovery: As template bindings are processed, paths are registered with
schema.addPath() - Observer Map Creation: If enabled, an
ObserverMapis instantiated with the populated schema - Property Definition: The observer map defines observable properties on the custom element prototype
Configuration
Observer Map functionality is enabled through the observerMap() definition
extension exported from @microsoft/fast-element/observer-map.js:
import { declarativeTemplate } from "@microsoft/fast-element/declarative.js";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
MyElement.define(
{
name: "my-custom-element",
template: declarativeTemplate(),
},
[observerMap()]
);When the properties key is omitted, all root properties discovered in the
template are observed. Add a properties object to opt into selective
observation behavior.
For non-declarative/manual schema use, pass the schema in the observer map
configuration:
import { FASTElement, Schema } from "@microsoft/fast-element";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
class MyElement extends FASTElement {}
const schema = new Schema("my-custom-element");
schema.addPath({
rootPropertyName: "user",
pathConfig: {
type: "default",
parentContext: null,
currentContext: null,
path: "user.name",
},
childrenMap: null,
});
MyElement.define(
{
name: "my-custom-element",
},
[observerMap({ schema })]
);You can also attach a manual schema to the FAST element definition. This is the
path used automatically by declarativeTemplate() after it parses a template:
MyElement.define(
{
name: "my-custom-element",
schema,
},
[observerMap()]
);Initial Path Processing Flow
Here's how binding paths flow through the system during initial template processing:
- Template Binding:
{{user.details.personal.name}} - Path Analysis: Utilities identify root property (
user) and nested path (details.personal.name) - Type Check: Confirms this is a data binding (not an event handler like
@click="{handleClick()}") - Schema Registration: typescript
schema.addPath({ pathConfig: { type: "access", currentContext: null, parentContext: null, path: "user.details.personal.name" }, rootPropertyName: "user", childrenMap: null }); - Schema Building: Creates nested object structure in JSON schema
- Observable Definition: Observer map makes
userproperty observable - Proxy Creation: When
useris assigned, proxies are created for deep observation
Event Handler and Context Path Exclusion
Event handlers are identified but explicitly excluded from schema processing:
- Template Binding:
@click="{handleClick()}" - Path Analysis: Utilities identify this as an event handler
- Type Check: Confirms this is type
"event" - Schema Registration: Skipped - Event handlers are not added to schemas since they represent function calls
Context paths ($c prefix) are also excluded from schema processing:
- Template Binding:
{{$c.parent.showNames}}or@click="{$c.parent.handleClick($c.event)}" - Path Analysis: Utilities detect the
$c.prefix - Schema Registration: Skipped - These paths resolve from the
ExecutionContext, not from observable data properties
Dynamic Path Processing Updates
After initial schema creation and observer map setup, the system continues to dynamically update observation as data structures evolve:
Runtime Object Assignment
When nested objects or array items become defined after initial setup:
Property Assignment: A previously undefined nested property gets assigned a value
typescript// Initially: user.details was undefined user.details = { age: 30, location: { city: "Seattle" } };Schema Consultation: The system consults the existing schema for this root property to understand expected nested structure
Proxy Creation: New proxies are automatically created for the newly assigned nested objects based on the schema definition
Deep Observation Extension: Observation is extended to all nested properties defined in the schema
Array Item Addition
When new items are added to observed arrays:
Array Mutation: Items are added via push, splice, or direct assignment
typescriptusers.push({ name: "John", details: { age: 25 } });Array Observer Trigger: The array proxy detects the mutation and triggers observation setup
Item Processing: Each new array item is processed according to the array's context schema definition
Nested Object Proxying: New nested objects within array items are automatically proxied based on the schema structure
Observable Proxy System
The proxy system in utilities.ts provides the deep observation capabilities:
assignObservables Function
export function assignObservables(
schema: JSONSchema | JSONSchemaDefinition,
rootSchema: JSONSchema,
data: any,
target: any,
rootProperty: string
): typeof ProxyThis function:
- Analyzes the data type (object, array, primitive)
- Creates proxies for objects and arrays
- Establishes property observation for nested structures
- Handles array mutations and object property changes
Proxy Behavior
Proxied objects:
- Trigger Observable notifications when properties change
- Automatically proxy newly assigned nested objects
- Track array mutations and splice operations
- Maintain references to root properties for change notifications
Examples
Basic Object Observation
Template:
<f-template name="user-profile">
<template>
<div>{{user.name}} - {{user.details.age}} years old</div>
<div>Location: {{user.details.location.city}}</div>
</template>
</f-template>Generated Schema Structure:
// Root property: "user"
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://fast.design/schemas/user-profile/user.json",
"type": "object",
"properties": {
"name": {},
"details": {
"type": "object",
"properties": {
"age": {},
"location": {
"type": "object",
"properties": {
"city": {}
}
}
}
}
}
}Array with Repeat Context
Template:
<f-template name="user-list">
<template>
<f-repeat value="{{user in users}}">
<div>{{user.name}} has {{user.posts.length}} posts</div>
</f-repeat>
</template>
</f-template>Generated Schema Structure:
// Root property: "users"
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://fast.design/schemas/user-list/users.json",
"type": "array",
"$ref": "#/$defs/user",
"$defs": {
"user": {
"$fast_context": "users",
"$fast_parent_contexts": [null],
"type": "object",
"properties": {
"name": {},
"posts": {
"type": "array"
}
}
}
}
}Nested Repeat Contexts
Template:
<f-template name="complex-data">
<template>
<f-repeat value="{{user in users}}">
<div>{{user.name}}</div>
<f-repeat value="{{post in user.posts}}">
<div>{{post.title}} - {{post.metadata.views}} views</div>
</f-repeat>
</f-repeat>
</template>
</f-template>This creates nested context definitions where the post context understands its relationship to the parent user context.
Technical Details
Schema Registry
The Schema module exports a module-level schemaRegistry:
export const schemaRegistry: CachedPathMap = new Map();Each Schema instance owns an instance-level schemaMap: Map<string, JSONSchema> and registers itself in schemaRegistry on construction.
The registry structure is: Map<customElementName, Map<rootPropertyName, JSONSchema>>
Rationale for Module-level Registry: The registry allows cross-element $ref resolution for nested components inside f-templates. When an object or array is passed to another custom element within an f-template, that nested component needs to observe the entire root property's structure based on the binding paths within that nested component. The registry allows all components to access and contribute to the same schema definitions, ensuring consistent observation behavior across component boundaries.
Context Tracking
The schema system tracks binding contexts using special metadata:
$fast_context: The array property name that created this context$fast_parent_contexts: Chain of parent contexts leading to the root
Performance Considerations
- Lazy Proxy Creation: Proxies are only created when objects are assigned to observable properties
- Weak Map Caching: The system uses WeakMaps to avoid memory leaks when tracking proxied objects
- Schema Reuse: Schemas are generated once per custom element and reused across instances
Memory Management
- WeakMap Usage:
objectTargetsMapandobservedArraysMapuse WeakMaps to automatically clean up references - Proxy Identification: Proxies are marked with
$isProxyto prevent double-proxying - Reference Tracking: Target elements and root properties are tracked for proper change notification
Debugging and Troubleshooting
Schema Inspection
You can inspect generated schemas using the module-level schemaRegistry import:
import { schemaRegistry } from "@microsoft/fast-element";
// Get all schemas for an element:
const elementSchemas = schemaRegistry.get('my-element');
const userSchema = elementSchemas?.get('users');
console.log(JSON.stringify(userSchema, null, 2));Observer Map Status
observerMap() is applied as a definition extension before the element is
registered. There is no public TemplateElement.options() state to inspect.
To verify that observer mapping ran, inspect the generated schema and the
observable accessors on the element prototype:
import { Observable, schemaRegistry } from "@microsoft/fast-element";
const schemas = schemaRegistry.get("my-element");
const accessors = Observable.getAccessors(MyElement.prototype).map(a => a.name);
console.log(schemas, accessors);Proxy Detection
Objects processed by the system will have a $isProxy property:
console.log('Is proxied:', myObject.$isProxy === true);Packages Fast Element Docs Declarative Syntax (`packages/fast-element/docs/declarative/syntax.md`)
FAST Element Declarative HTML
The declarative entrypoint in @microsoft/fast-element interprets FAST
declarative HTML syntax as a template for a FAST web component.
This document focuses on declarative-runtime implementation details:
template structure, prerendered markup requirements, hydration promise APIs,
binding configuration, syntax, and integration testing.
For package installation, using declarativeTemplate(), extension setup, and
the package-level hydration overview, see the
FAST Element README and
Prerendered Content Optimization.
For user-facing guides covering f-template syntax, element definition, and
server-side rendering, see the
Declarative HTML docs.
Template Structure
After importing the declarative APIs as shown in the README, templates are
associated with an element through<f-template name="[custom-element-name]"><template>...</template></f-template>.
The host custom element should be defined withtemplate: declarativeTemplate(). This automatically defines <f-template> in
the relevant registry and waits for the matching declarative template when it is
already present or inserted later.
The @microsoft/fast-element entrypoint itself remains
side-effect free at import time. Declarative APIs lazily install declarative
debug messages when they create templates. Hydratable ViewTemplate support is
installed only when enableHydration() is called from@microsoft/fast-element/hydration.js.
observerMap() and attributeMap() are available from@microsoft/fast-element/observer-map.js and@microsoft/fast-element/attribute-map.js, including when using the maps
without declarative templates.
Example:
<my-custom-element greeting="Hello world">
<template shadowrootmode="open">
Hello world
</template>
</my-custom-element>
<f-template name="my-custom-element">
<template>{{greeting}}</template>
</f-template>The legacy defer-hydration and needs-hydration attributes are no longer
required.
HTML Comments
Bindings and FAST directive markup inside HTML comments are unsupported. This
includes double-brace and triple-brace bindings, client-only bindings, and
commented-out elements containing bindings or directives.
The declarative parsers scan template strings and do not reliably exclude
comments. A comment such as `` can still contribute a binding,
leading to unexpected evaluation, rendering errors, or hydration mismatches.
Remove unused bound markup rather than commenting it out, and keep explanatory
comments free of binding syntax and FAST directive tags.
Non-browser HTML Rendering
One of the benefits of FAST declarative HTML templates is that the server can
be stack agnostic because JavaScript does not need to be interpreted to emit
the initial HTML. This does not mean the FAST Element client runtime runs on the
server: custom element definition, hydration, client rendering, and reactive
updates still run in the browser Window. The declarative runtime expects
hydratable comment markers and datasets when prerendered content is generated.
For the required marker format and initial-state application details, see
rendering.md.
Hydration promise APIs
FAST Element's declarative APIs expose hydration readiness promises that are
useful to application code.
Hydration is opt-in. Call enableHydration() before FAST elements connect when
you want prerendered Declarative Shadow DOM to be reused, then await
the returned controller's whenHydrated() promise when code needs to run after
the active hydration batch:
import { enableHydration } from "@microsoft/fast-element/hydration.js";
const hydration = enableHydration();
await hydration.whenHydrated();The hydration hook no-ops for new prerendered elements after the initial
hydration batch completes by default. Streaming scenarios that append hydratable
Declarative Shadow DOM later can keep the hook active:
import { enableHydration, StopHydration } from "@microsoft/fast-element/hydration.js";
enableHydration({
stopHydration: StopHydration.never,
});When StopHydration.never is used, enableHydration().whenHydrated()
intentionally remains pending because hydration has no global completion point.
FAST Element 3.x data-free hydration markers are selected by default. Existing
server output that uses FAST Element 2.x indexed markers can be hydrated by
selecting the v2 marker reader:
import { enableHydration, markers_v2 } from "@microsoft/fast-element/hydration.js";
enableHydration({ markers: markers_v2 });Set "markers_v2": true in the matching @microsoft/fast-build
configuration so the generated HTML uses the same marker format.
`observerMap`
When the observerMap() extension is applied to an element definition,
it automatically sets up deep reactive observation for root properties
discovered in the template. Declarative templates assign definition.schema
during template resolution, so observerMap() has schema data automatically.
For non-declarative/manual schemas, import from@microsoft/fast-element/observer-map.js and pass observerMap({ schema }).
import { observerMap } from "@microsoft/fast-element/observer-map.js";For finer control, pass a configuration object with a properties key that maps root property names to a recursive path tree:
UserProfile.define(
{
name: "user-profile",
template: declarativeTemplate(),
},
[
observerMap({
properties: {
user: {
name: true, // user.name — observed
details: {
age: true, // user.details.age — observed
history: false, // user.details.history — NOT observed
},
},
// root properties not listed here are skipped
},
}),
],
);Each path entry can be:
true— observe this path and all descendants.false— skip this path and all descendants.- An object with an optional
$observeboolean and child property overrides.
Use $observe: false on a node to skip it by default, then re-include specific children:
observerMap({
properties: {
analytics: {
charts: {
$observe: false, // charts NOT observed by default
activeChart: true, // ...except activeChart IS observed
},
},
},
});When properties is omitted, all root properties are observed. Whenproperties is present but empty ({ properties: {} }), no root properties
are observed.
When observer-map data is updated with deepMerge, array properties are replaced
rather than updated in place. This avoids synchronous reentrant array work and
allows repeat bindings to observe the new array reference. Replacement arrays
are processed through observerMap so nested item properties and subsequent array
mutations remain observable.
Manual schema example:
import { FASTElement, Schema } from "@microsoft/fast-element";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
class MyElement extends FASTElement {}
const schema = new Schema("my-element");
schema.addPath({
rootPropertyName: "user",
pathConfig: {
type: "default",
parentContext: null,
currentContext: null,
path: "user.name",
},
childrenMap: null,
});
MyElement.define({ name: "my-element" }, [observerMap({ schema })]);`attributeMap`
When the attributeMap() extension is applied to an element definition,
it automatically creates reactive @attr properties for every leaf binding
in the template — simple expressions like {{foo}} or id="{{fooBar}}" that
have no nested properties. Declarative templates provide the schema
automatically. For non-declarative/manual schemas, place the optional schema
on the FAST element definition and import attributeMap() from@microsoft/fast-element.
import { attributeMap } from "@microsoft/fast-element/attribute-map.js";By default, the binding key is treated as a camelCase property name and the HTML
attribute name is derived by converting it to kebab-case. Properties already
decorated with @attr or @observable on the class are left untouched.
MyElement.define(
{
name: "my-element",
template: declarativeTemplate(),
},
[attributeMap()],
);With the template:
<f-template name="my-element">
<template>
<p>{{greeting}}</p>
<p>{{firstName}}</p>
</template>
</f-template>This registers greeting (attribute greeting, property greeting) andfirstName (attribute first-name, property firstName) as @attr
properties on the element prototype, enabling setAttribute("first-name", "Jane") to trigger a template re-render automatically.
`attribute-name-strategy`
The attribute-name-strategy configuration option controls how template binding
keys map to HTML attribute names. This matches the build-time--attribute-name-strategy option in @microsoft/fast-build.
| Strategy | Behaviour | Example |
|---|---|---|
"camelCase" (default) |
Binding key is the camelCase property; attribute name is derived as kebab-case | {{fooBar}} → property fooBar, attribute foo-bar |
"none" |
Binding key used as-is for both property and attribute | {{foo-bar}} → property foo-bar, attribute foo-bar |
MyElement.define(
{
name: "my-element",
template: declarativeTemplate(),
},
[
attributeMap({
"attribute-name-strategy": "none",
}),
],
);When using the "none" strategy, property names may contain dashes and must be
accessed via bracket notation (e.g. element["foo-bar"]).
Syntax
All bindings use a handlebars-like syntax.
Some bindings are only relevant to the browser, such as for click handlers or other pieces of dynamic interaction. As such, their bindings use single curly braces {}, this is to prevent an intial SSR (Server Side Rendering) or other build time rendering technologies from needing to interpret them.
If a binding is relevant to both client and the back end rendering engine, it will use {{}} or {{{}}} depending on what type of data injection is being done.
Browser-only bindings:
- Event bindings
- Attribute directives
Content binding
{{text}}Event binding
Event bindings must include the () as well as being preceeded by @ in keeping with the @microsoft/fast-element tagged template syntax.
<button @click="{handleClick()}"></button>You can pass the DOM event object, the execution context, or both as arguments. Any other argument is treated as a binding expression and resolved against the current data source.
$e — DOM event object (preferred):
<button @click="{handleClick($e)}"></button>$c — execution context:
<button @click="{handleClick($c)}"></button>$c.somePath — a property of the execution context (e.g. $c.parent, $c.event):
<button @click="{handleClick($c.parent)}"></button>Multiple arguments:
<button @click="{handleClick($e, $c)}"></button>Arbitrary binding expressions — any token that is not $e or $c is resolved as a binding path on the data source:
<button @click="{handleClick(user.id)}"></button>Use $e for the DOM event object.
Directives
Directives are assumed to be either an attribute directive or a directive that also serves a template. Both are prepended by f-. The logic of these directives and what their use cases are is explained in the FAST html documentation.
Attribute directives are part of client side binding and therefore use the {} syntax.
Attribute directives include:
slotted
Example:
html<slot f-slotted="{slottedNodes}"></slot> <slot f-slotted="{slottedNodes filter elements()}"></slot> <slot f-slotted="{slottedNodes filter elements(div, p)}"></slot>children
Example:
html<ul f-children="{listItems}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul> <ul f-children="{listItems filter elements()}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul> <ul f-children="{listItems filter elements(li)}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>ref
Example:
html<video f-ref="{video}"></video>
Template directives include:
when
Example:
html<f-when value="{{show}}">Hello world</f-when> <f-when value="{{!show}}">Goodbye world</f-when>
The following operators can also be used:
==!=>=><=<||&&
Where the right operand can be either a reference to a value (string e.g. {{foo == 'bar'}}, boolean e.g. {{foo == true}}, number e.g. {{foo == 3}}) or another binding value.
repeat
Example:
html<ul><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>Bindings inside
<f-repeat>without a context prefix resolve to the custom element. For example,{{title}}below resolves to the host element'stitleproperty:html<ul><f-repeat value="{{item in list}}"><li>{{item}} - {{title}}</li></f-repeat></ul>
Execution Context Access
In imperative fast-element templates, every binding expression receives both the data source and the execution context: ${(x, c) => c.parent.handleClick(c.event)}. Declarative <f-template> expressions can access the same execution context using the $c prefix.
This is particularly useful inside <f-repeat>, where $c.parent refers to the parent view-model (typically the host element) and $c.event provides the DOM event.
Event handler with context access:
<f-repeat value="{{item in items}}">
<button @click="{$c.parent.handleItemClick($c.event)}">{{item.name}}</button>
</f-repeat>Conditional rendering using a host property inside a repeat:
<f-repeat value="{{item in items}}">
<f-when value="{{$c.parent.showNames}}">
<span>{{item.name}}</span>
</f-when>
</f-repeat>Unescaped HTML
You can add unescaped HTML using triple braces, this will create an additional div element as the HTML needs an element to bind to. Where possible it is advisable to not use unescaped HTML and instead use other binding techniques.
Example:
{{{html}}}Code samples inside ``
Any text or attribute value inside a <code> element has its { and } characters automatically replaced with the HTML numeric character references { and } before template parsing. As a result, binding delimiters ({{...}}, {{{...}}}, {...}) inside <code> are rendered as literal text rather than being interpreted as FAST bindings — making <code> blocks safe for embedding code samples that contain binding-like syntax. No marker attribute is needed.
In addition, the build-time renderer (@microsoft/fast-build) entity-escapes the < and > of every FAST directive tag (<f-when>, </f-when>, <f-repeat>, </f-repeat>) it finds inside <code>, so authors can write the directive literally without manually entity-encoding the angle brackets. Tag-name matching is case-insensitive. Non-directive elements inside <code> — real HTML elements such as `` and custom elements such as <my-widget> — keep their angle brackets and continue to render as live DOM elements; only brace-binding syntax in their attribute values is neutralised.
The brace escape is applied symmetrically by both the server-side renderer and the client-side <f-template> parser, so binding positions stay in sync between SSR and hydration. The directive-tag angle escape only needs to run on the server: the DOM serializer re-encodes </> in text content, so the client-side parser never sees a raw <f-when> inside <code> regardless of what the page source contained. This behaviour mirrors how Microsoft WebUI's webui-press markdown renderer transparently escapes the same characters inside code spans and code fences.
Example:
<f-template name="docs-page">
<template>
<h1>{{title}}</h1>
<pre><code>span {{greeting}} /span</code></pre>
<pre><code><f-when value="{{flag}}">wrapped</f-when></code></pre>
<pre><code>Try: <button class="demo">click</button></code></pre>
</template>
</f-template>The <h1> binds to title, the first <code> displays the literal text span {{greeting}} /span, the second displays the literal text <f-when value="{{flag}}">wrapped</f-when>, and the third renders a live `` element alongside the surrounding sample text.
Writing Components
When writing components with the intention of using the declarative HTML syntax, it is imperative that components are written with styling and rendering of the component to be less reliant on any JavaScript state management. An example of this is relying on elementInterals state to style a component.
WebUI Integration Testing
The fixture tests in test/declarative/fixtures/ are also validated against@microsoft/webui to ensure
cross-renderer compatibility. The build step renders each fixture's templates
with webui build --plugin=fast, then the existing Playwright specs run
against the webui-rendered output.
# Build fixtures with webui and run Playwright tests
npm run test:webui-integration -w @microsoft/fast-element
# Or run the steps separately
npm run build:fixtures:webui -w @microsoft/fast-element
npm exec -w @microsoft/fast-element -- playwright test --config=playwright.declarative.webui.config.tsThis is also run automatically in CI via the ci-webui-integration.yml GitHub Action on pull requests and pushes to main.
Some tests are conditionally skipped during webui integration runs due to known
rendering differences between fast-build and webui. Theplaywright.declarative.webui.config.ts file setsFAST_WEBUI_INTEGRATION=true, and affected tests use test.skip() with a
descriptive reason. See the WebUI Integration Tests section in
design.md for the full list.
Packages Fast Element Docs Di Api Report.Api (`packages/fast-element/docs/di/api-report.api.md`)
API Report File for "@microsoft/fast-element"
Do not edit this file. It is a report generated by API Extractor.
/* Detailed source-code truncated for AI context efficiency. */Packages Fast Element Docs Migration Fast Element 2 (`packages/fast-element/docs/migration/fast-element-2.md`)
Changes in FASTElement 2.0
Breaking Changes
HTMLDirective- ThetargetIndex: numberproperty has been replaced by atargetId: stringproperty. ThecreateBehaviormethod no longer takes a targetNodebut instead takes aBehaviorTargetsinstance. The actual target can be looked up on theBehaviorTargetsinstance by indexing with thetargetIdproperty.compileTemplate()- Internals have been significantly changed. The implementation no longer uses a TreeWalker. The return type has change to anHTMLTemplateCompilationResultwith different properties.ViewandHTMLView- Type parameters added to enable strongly typed views based on their data source. The constructor ofHTMLViewhas a new signature based on changes to the compiler's output. Internals have been cleaned up and no longer rely on the Range type.ElementViewTemplate,SyntheticViewTemplate, andViewTemplate- Added type parameters throughout. Logic to instantiate and apply behaviors moved out of the template and into the view where it can be lazily executed. Removed the ability of therendermethod to take a string id of the node to render to. You must provide a node.DOM- Tree Walker methods are no longer used and are thus removed. The API for removing child nodes has been removed as well since it was only used in one place and could be inlined. The helpercreateCustomAttributePlaceholder()no longer requires an attribute name. It will be uniquely generated internally.class- Bindings toclassare now more nuanced. Binding directly toclasswill simply set theclassNameproperty. If you need to bind toclassknowing that manual JS will also manipulate theclassListin addition to the binding, then you should now bind to:classListinstead. This allows for performance optimizations in the simple, most common case.BehaviorandViewBehavior-Behaviornow requires anExecutionContextforunbind. Behaviors can be used for elements or views.ViewBehaviorhas been introduced for use exclusively with views, and provides some optimization opportunities.RefBehaviorhas been replaced withRefDirective. The directive also implementsViewBehaviorallowing a single directive instance to be shared across all template instances that use the ref.- Removed
SlottedBehaviorandChildrenBehaviorhave been replaced withSlottedDirectiveandChildrenDirective. These directives allow a single directive instance to be shared across all template instances that use the ref. - Removed
AttachedBehaviorHTMLDirectiveandAttachedBehaviorTypesince they are no longer used in the new directive/behavior architecture for ref, slotted, and children. - Renamed
Notifier#sourcetoNotifier#subjectto align with other observable terminology and prevent name clashes withBindingObserverproperties.
Packages Fast Element Docs Migration Fast Element 3 (`packages/fast-element/docs/migration/fast-element-3.md`)
Migrating from previous versions
FASTGlobal version tracking (v2 → v3)
Removed API
| Removed | Replacement |
|---|---|
FAST.versions |
No replacement. Multiple FAST versions on the same page are unsupported in v3. |
Removed configuration
| Removed | Replacement |
|---|---|
fast-kernel="share" / fast-kernel="share-v2" / fast-kernel="isolate" |
No replacement. FAST now uses a single shared kernel contract, and multiple FAST versions on the same page are unsupported. |
Migration steps
- Remove any runtime checks that read
FAST.versions. - Fix duplicate FAST installs in your bundler or dependency graph instead of relying on version tracking at runtime.
- Remove any
fast-kernelscript attributes. They no longer affect FAST initialization.
Native globals and scheduler requirements (v2 → v3)
Changed behavior
- Browser
Windowruntime required:@microsoft/fast-elementis a
client-side browser runtime. It expects browser DOM, Custom Elements, Shadow
DOM, and scheduling APIs such asrequestAnimationFrame. - Native
globalThisrequired:@microsoft/fast-elementno longer installs
aglobalThispolyfill as a side effect. - No idle-callback polyfill: FAST Element v3 no longer installs or depends
on arequestIdleCallback/cancelIdleCallbackfallback. The asyncUpdatesqueue schedules work withglobalThis.requestAnimationFrame.
Migration steps
- Verify that the browsers you support provide native
globalThisandrequestAnimationFrame. - If you still target an older browser without
globalThis, load that polyfill
before importing@microsoft/fast-element. - Do not import or run the FAST Element client runtime in workers,
server-side JavaScript runtimes, or other non-window hosts. SSR tools may
emit HTML, but hydration and client rendering run in the browserWindow. - You do not need to polyfill
requestIdleCallbackorcancelIdleCallback
for FAST Element itself. Only provide them if your application or another
dependency calls those APIs directly.
Declarative HTML moved into fast-element (v3)
Declarative HTML APIs now ship from focused @microsoft/fast-element package
path exports instead of the removed @microsoft/fast-html package.
Import changes
| Before | After |
|---|---|
@microsoft/fast-html |
@microsoft/fast-element/declarative.js |
@microsoft/fast-html/utilities.js |
@microsoft/fast-element/declarative-utilities.js |
Core FAST Element helpers are available from the root package export:
| API | Import path |
|---|---|
Updates |
@microsoft/fast-element |
Observable, observable |
@microsoft/fast-element |
attr, AttributeDefinition, converters, ValueConverter |
@microsoft/fast-element |
Binding, oneWay, oneTime, listener |
@microsoft/fast-element |
DOM, DOMAspect, DOMPolicy |
@microsoft/fast-element |
Schema, schemaRegistry, schema types |
@microsoft/fast-element |
css |
@microsoft/fast-element |
html, ViewTemplate, HTMLView |
@microsoft/fast-element |
Compiler, HTMLDirective, htmlDirective, templating/view types |
@microsoft/fast-element |
render, RenderBehavior, RenderDirective |
@microsoft/fast-element |
enableHydration, deferHydrationAttribute, HydrationTracker, hydration types |
@microsoft/fast-element/hydration.js |
ArrayObserver |
@microsoft/fast-element |
volatile |
@microsoft/fast-element |
children |
@microsoft/fast-element |
elements, NodeObservationDirective |
@microsoft/fast-element |
ref |
@microsoft/fast-element |
slotted |
@microsoft/fast-element |
when |
@microsoft/fast-element |
repeat |
@microsoft/fast-element |
Migration steps
- Update declarative runtime imports to
@microsoft/fast-element/declarative.js. - Update declarative utility imports such as
deepMergeto@microsoft/fast-element/declarative-utilities.js. - Keep importing FAST Element APIs such as
FASTElement,FAST,ElementController, definition/controller types,attr,Schema, andobservablefrom@microsoft/fast-element. - Call
enableHydration()from@microsoft/fast-element/hydration.jswhen
prerendered content should be hydrated.
`deepMerge` array replacement
When an array's contents change, deepMerge replaces the owning property's
array rather than mutating the existing array in place. This affects bothobserverMap()-managed data and direct consumers of the exported helper.
Previously captured array references retain the old data; mutating them no
longer updates the owning object. Deeply equal arrays are left unchanged.
Re-read the array from its owner after merging:
import { deepMerge } from "@microsoft/fast-element/declarative-utilities.js";
const user = { orders: [{ id: 101 }] };
const oldOrders = user.orders;
deepMerge(user, { orders: [{ id: 103 }] });
// oldOrders still contains order 101 and is disconnected from user.orders.
user.orders.push({ id: 104 });For observerMap()-managed properties, the owning property's notification
causes f-repeat bindings to observe the replacement array automatically.
Direct deepMerge consumers must still arrange their own change notifications;
the helper does not make plain objects observable.
Optional helper and removed path exports (v2 → v3)
FAST Element v3 adds focused flat path exports for optional helpers and removes
older nested or unused package exports. Root imports for core FAST Element APIs
remain supported, but code that imports helper APIs from older nested paths must
move to the flat path exports.
Import changes
| Before | After |
|---|---|
@microsoft/fast-element/binding/two-way.js |
@microsoft/fast-element/two-way.js |
@microsoft/fast-element/binding/signal.js |
@microsoft/fast-element/signal.js |
| Deep directive imports | @microsoft/fast-element/children.js, @microsoft/fast-element/repeat.js, @microsoft/fast-element/when.js, @microsoft/fast-element/ref.js, @microsoft/fast-element/slotted.js, or @microsoft/fast-element/node-observation.js |
| Declarative map helpers from old declarative surfaces | @microsoft/fast-element/attribute-map.js and @microsoft/fast-element/observer-map.js |
Removed package exports
| Removed | Replacement |
|---|---|
@microsoft/fast-element/install-element-hydration.js |
No side-effect import. Call enableHydration() from @microsoft/fast-element/hydration.js when SSR content should hydrate. |
@microsoft/fast-element/install-hydratable-view-templates.js |
No side-effect import. enableHydration() installs hydratable template support. |
@microsoft/fast-element/element-hydration.js |
@microsoft/fast-element/hydration.js for public hydration APIs. |
@microsoft/fast-element/metadata.js |
No supported public replacement. |
@microsoft/fast-element/testing.js |
No supported public replacement. |
@microsoft/fast-element/pending-task.js |
No supported public replacement. |
Migration steps
- Replace older nested helper imports with the flat path exports shown above.
- Remove hydration side-effect imports and call
enableHydration()before FAST
elements connect when SSR content should hydrate. - Remove imports from deleted package exports. If your app depended on one of
those internals, move that usage into app-owned code or a supported public
API.
`attribute-name-strategy` default changed (v2 → v3)
The declarative attribute map default changed from "none" to "camelCase".
With the default strategy, a binding key such as {{firstName}} maps to thefirstName property and the first-name HTML attribute. In v2-style
declarative templates that relied on literal attribute names, this can change
which property receives data.
Migration steps
- Audit declarative templates for binding keys that contain hyphens or that
intentionally matched attribute names exactly. - If you want the v2 behavior, configure the client definition extension with
attributeMap({ "attribute-name-strategy": "none" }). - If server-rendered HTML is generated with
@microsoft/fast-build, pass--attribute-name-strategy=noneso server and client mapping agree.
`TemplateOptions` removal (v3)
Removed APIs
| Removed | Replacement |
|---|---|
TemplateOptions |
No replacement |
PartialFASTElementDefinition.templateOptions |
No replacement |
FASTElementDefinition.templateOptions |
No replacement |
Changed behavior
- Subclass
define()calls no longer usetemplateOptionsto delay platform
definition or connection. - Elements can still be defined before a template is attached; a later
FASTElementDefinition.templateupdate notifies connected elements so they
can render or hydrate with the new template.
Migration steps
- Remove
templateOptionsfrom element definitions. - Continue calling
define({ name })when a definition needs to exist before
its template is attached. - If a template is supplied later, assign
FASTElementDefinition.template(or
use the declarative runtime that does so for you).
`FASTElementDefinition.isRegistered` removal (v3)
Removed APIs
| Removed | Replacement |
|---|---|
FASTElementDefinition.isRegistered |
fastElementRegistry.whenRegistered(tagName), fastElementRegistry.getByType(type), or fastElementRegistry.getForInstance(instance) |
Migration steps
- Import
fastElementRegistryfrom@microsoft/fast-element/registry.js. - Use
fastElementRegistry.whenRegistered(tagName)when code needs to wait for
a definition by tag name. - Use
fastElementRegistry.getByType(type)orfastElementRegistry.getForInstance(instance)when code already has a
constructor or element instance.
Declarative TemplateElement API removal (v3)
Removed APIs
| Removed | Replacement |
|---|---|
TemplateElement public export |
declarativeTemplate() on each FAST element definition |
TemplateElement.define({ name: "f-template" }) |
No manual definition; declarativeTemplate() defines FAST's internal <f-template> publisher in the target registry |
TemplateElement.config(callbacks) / HydrationLifecycleCallbacks |
enableHydration().whenHydrated(tagName) for tag-specific hydration waits and enableHydration().whenHydrated() for the active hydration batch |
TemplateElement.options(...), ElementOptions, ElementOptionsDictionary |
Define extensions: attributeMap(...) and observerMap(...) passed as the second argument to define() |
AttributeMap / ObserverMap exports from the old declarative public surface |
attributeMap() / observerMap() extension helpers and their config types |
Migration steps
Replace manual
<f-template>registration withtemplate: declarativeTemplate():typescript// Before import { TemplateElement } from "@microsoft/fast-element"; TemplateElement.define({ name: "f-template" }); MyElement.define({ name: "my-element" }); // After import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; MyElement.define({ name: "my-element", template: declarativeTemplate(), });Replace
TemplateElement.options()with definition extensions:typescriptimport { 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()], );attributeMap()is imported from@microsoft/fast-element/attribute-map.jsandobserverMap()from@microsoft/fast-element/observer-map.jsfor declarative templates,
manually supplied schemas, or both.FASTElementDefinition.schemais optional;declarativeTemplate()assigns it automatically, andobserverMap()can
take a manual schema withobserverMap({ schema }).Replace
TemplateElement.config()withenableHydration().whenHydrated(tagName)
for tag-specific hydration waits andenableHydration().whenHydrated()for the
active hydration batch.
Hydration is not installed bydeclarative.js; callenableHydration()
before elements connect when SSR content should hydrate.
Debug entrypoint explicit enablement (v3)
Import changes
| Before | After |
|---|---|
import "@microsoft/fast-element"; |
import { enableDebug } from "@microsoft/fast-element/debug.js"; enableDebug(); |
Migration steps
- Replace setup-only
debug.jsimports with an explicitenableDebug()call. - Keep using the root package
developmentexport or debug rollup bundle when
you want debug behavior enabled automatically.
Declarative event handler `e` removal (v3)
Removed behavior
| Removed | Replacement |
|---|---|
Bare e event arguments in declarative event handlers |
$e |
TemplateParser.hasDeprecatedEventSyntax |
No replacement |
Only $e and $c are reserved event handler arguments in declarative
templates. Bare e now resolves like any other binding path on the current
data source.
Migration steps
- Replace declarative event bindings such as
@click="{handleClick(e)}"with@click="{handleClick($e)}". - Remove any
TemplateParser.hasDeprecatedEventSyntaxchecks or warnings from
custom tooling.
Prerendered Content Optimization (v2 → v3)
Removed and moved APIs
| 2.x API | 3.x guidance |
|---|---|
HydratableElementController |
ElementController (prerendered path built in) |
HydrationControllerCallbacks |
Call enableHydration() and await the returned controller's whenHydrated() promise |
needsHydrationAttribute |
No replacement. Existing shadow root detection sets ElementController.isPrerendered. |
Root deferHydrationAttribute export |
Import deferHydrationAttribute from @microsoft/fast-element/hydration.js only if you still need the legacy attribute string. New server-rendered markup should omit defer-hydration. |
Removed side-effect imports
| Import | Replacement |
|---|---|
@microsoft/fast-element/install-hydration.js |
No replacement needed — prerendered path is built into ElementController |
@microsoft/fast-element/install-hydratable-view-templates.js |
No replacement needed — enableHydration() installs hydratable template support |
Use enableHydration() from @microsoft/fast-element/hydration.js when SSR
content should hydrate. The declarative entrypoint no longer installs hydration
automatically.
Changed behavior
attributeChangedCallbackduring upgrade: WhenisPrerenderedis true and the element has not yet connected, attribute change callbacks are suppressed. After connection, all attribute changes are processed normally.- Declarative template resolution:
declarativeTemplate()waits for the
matching<f-template>beforedefine()completes, so connected elements
hydrate with a concrete template. Nodefer-hydrationattribute is needed. - Binding evaluation with existing shadow root: When an existing shadow root is detected,
attributeandbooleanAttributebindings skip their initial DOM update. All other binding types (event, content, property, tokenList) run normally.
New APIs
ElementController.isPrerendered(Promise): Resolves totruewhen the element had a declarative shadow root at connect time, orfalsewhen the component was client-side rendered.ElementController.isHydrated(Promise): Resolves totrueonly when hydration ran successfully.ViewController.isPrerendered(Promise | undefined): Available to custom directives. Resolves totruewhen the view's content was prerendered,falseotherwise.ViewController.isHydrated(Promise | undefined): Available to custom directives. Resolves totrueonly when hydration ran successfully.
Migration steps
- Remove
HydratableElementController.install()calls. - Remove
import "@microsoft/fast-element/install-hydration.js"side-effect imports. - Replace
element.$fastController instanceof HydratableElementControllerchecks withawait element.$fastController.isPrerendered. - Remove
defer-hydrationandneeds-hydrationattributes from server-rendered markup.
Hydration Marker Format (v3)
Changed format
The hydration markers embedded in SSR output have been simplified from verbose, index-embedded comment markers to compact, data-free sequential markers.
Comment markers
| Marker type | Old format | New format |
|---|---|---|
| Content binding start | `` | `` |
| Content binding end | `` | `` |
| Repeat item start | `` | `` |
| Repeat item end | `` | `` |
| Element boundary start | `` | `` |
| Element boundary end | `` | `` |
Attribute binding markers
| Old format | New format |
|---|---|
data-fe-b="0 1 2" (space-separated indices) |
data-fe="N" (binding count) |
data-fe-b-0 (enumerated, one per factory) |
data-fe="N" |
data-fe-c-0-3 (compact, start index + count) |
data-fe="N" |
Removed APIs
| Export | Replacement |
|---|---|
HydrationMarkup.contentBindingStartMarker(index, scopeId) |
HydrationMarkup.contentBindingStartMarker() |
HydrationMarkup.contentBindingEndMarker(index, scopeId) |
HydrationMarkup.contentBindingEndMarker() |
HydrationMarkup.isContentBindingStartMarker(data) |
HydrationMarkup.isContentBindingStartMarker(data) (unchanged signature, new implementation) |
HydrationMarkup.isContentBindingEndMarker(data) |
HydrationMarkup.isContentBindingEndMarker(data) (unchanged signature, new implementation) |
HydrationMarkup.parseAttributeBinding(element) |
HydrationMarkup.parseAttributeBindingCount(element) |
HydrationMarkup.parseRepeatStartMarker(data) |
HydrationMarkup.isRepeatViewStartMarker(data) |
HydrationMarkup.parseRepeatEndMarker(data) |
HydrationMarkup.isRepeatViewEndMarker(data) |
HydrationMarkup.parseElementBoundaryStartMarker(content) |
HydrationMarkup.isElementBoundaryStartMarker(node) |
HydrationMarkup.parseElementBoundaryEndMarker(content) |
HydrationMarkup.isElementBoundaryEndMarker(node) |
Impact
This is a breaking change for SSR output format. Any system that produces or parses hydration markers must be updated to use the new format. The @microsoft/fast-build Rust crate and WASM binary have been updated accordingly.
- Marker parsing uses string equality checks (
data === "fe:b") instead of regex - Start/end pairing uses balanced depth counting instead of embedded IDs
- The hydration walker uses a sequential factory pointer instead of index-based lookup
- SSR and client versions must match — mixing old SSR output with new client code (or vice versa) will fail
Async define/compose/register API (v3)
Removed APIs
| Removed | Replacement |
|---|---|
FASTElement.defineAsync() |
Subclass define() calls |
FASTElement.compose() and subclass compose() calls |
Subclass define() calls |
FASTElementDefinition.composeAsync() |
Subclass define() calls |
FASTElementDefinition.registerAsync() |
FASTElementDefinition.register() (same Promise<Function> return type) |
Changed behavior
- Subclass
define()calls returnPromise<TType>only for code that
explicitly needs to observe registration completion. Whentemplate: declarativeTemplate()is used, that Promise resolves after the
matching<f-template>supplies the concrete template. - Subclass compose helpers are no longer part of the public authoring surface; use subclass
define()for registration. @customElementdecorator callsdefine()internally but does not return the Promise (fire-and-forget). For complete definitions with a template, the element is registered via a microtask.
Migration steps
Replace
defineAsync()calls withdefine():typescript// Before MyElement.defineAsync({ name: "my-element", templateOptions: "defer-and-hydrate", }); // After MyElement.define({ name: "my-element", template: declarativeTemplate(), });Replace subclass
compose()calls that immediately register an element withdefine():typescript// Before MyElement.compose({ name: "my-element", template, styles, }).define(); // After MyElement.define({ name: "my-element", template, styles, });Replace
FASTElementDefinition.composeAsync()calls that register an element
with subclassdefine():typescript// Before (await FASTElementDefinition.composeAsync(MyElement, { name: "my-element", template, styles, })).define(); // After MyElement.define({ name: "my-element", template, styles, });Replace
registerAsync()calls withregister():typescript// Before const el = await FASTElementDefinition.registerAsync(name); // After const el = await FASTElementDefinition.register(name);Replace
FASTElementDefinition.compose()calls that chain.define()with
subclassdefine():typescript// Before FASTElementDefinition.compose(MyElement, options).define(); // After MyElement.define(options);
Dynamic stylesheet behaviors (v3)
Removed API
| Removed | Replacement |
|---|---|
ElementStyles.withBehaviors() |
Move the runtime condition into the element and call this.$fastController.addStyles() / this.$fastController.removeStyles() directly. |
ElementStyles.behaviors |
Move any runtime behavior out of the stylesheet and into the element or controller lifecycle. |
CSS bindings in css (for example ``css`color: ${x => x.color}``` |
|
| /* Detailed source-code truncated for AI context efficiency. */ |
toView(value: any) {
return value ? "" : null;
}
fromView(value: any) {
return !!value;
}booleanConverter v2 → v3 result differences:
| Call | v2 result | v3 result |
|---|---|---|
fromView("false") |
false |
true |
fromView("") |
true |
false |
fromView(NaN) |
true |
false |
toView(true) (or any truthy) |
"true" |
"" |
toView(false) (or any falsy) |
"false" |
null |
Note: For
mode: "boolean", FAST writes attributes viaDOM.setBooleanAttribute(presence-based) and reads them asvalue !== null, so themode: "boolean"reflection path does not route throughbooleanConverter.toView()orbooleanConverter.fromView(). The converter methods still apply to property assignment (el.bool = XrunsfromView(X)), explicit calls (booleanConverter.fromView(x)), andbooleanConverterpaired withmode: "reflect"(which callstoView).
Migration steps
- Audit
boolean-mode attributes for the literal string"false"in templates or SSR HTML — the resulting property will betrue, notfalse. To express the falsy state, omit the attribute entirely (or callelement.removeAttribute(name)). Settingelement.bool = falsecontinues to remove the reflected attribute. - Audit code that calls
booleanConverter.toView()directly or pairsbooleanConverterwithmode: "reflect". The output strings are no longer"true"/"false". - If you need a tri-state attribute (
true/false/ unset) that preserves an explicit"false"string value, usenullableBooleanConverterwithmode: "reflect"instead ofmode: "boolean".
Packages Fast Element Docs Migration Fast Html (`packages/fast-element/docs/migration/fast-html.md`)
Migrating from previous versions
Hydration Marker Format (v1-alpha → v1)
The SSR output format for hydration markers has been simplified. If you have any tooling that inspects or generates hydration markers, update it to use the new format.
| Old marker | New marker |
|---|---|
| `` | `` |
| `` | `` |
| `` | `` |
| `` | `` |
data-fe-b="0 1 2" / data-fe-b-0 / data-fe-c-0-3 |
data-fe="N" |
The @microsoft/fast-build WASM binary emits the new markers automatically. Rebuild all fixtures and SSR output after upgrading.
See the @microsoft/fast-element migration guide for full details on API changes.
Prerendered Content Optimization (v1-alpha → v1)
Removed exports
| Export | Replacement |
|---|---|
RenderableFASTElement |
Extend FASTElement |
Removed concepts
| Concept | Replacement |
|---|---|
prepare() lifecycle hook |
Set state in connectedCallback; the reactive system updates the DOM |
defer-hydration attribute in rendered markup |
Template-pending guard in ElementController.connect() |
needs-hydration attribute in rendered markup |
hasExistingShadowRoot detection in ElementController |
waitForAncestorHydration() |
Not needed — prerendered content is correct regardless of connection order |
Migration steps
Replace
RenderableFASTElement(MyComponent).defineAsync({...})withMyComponent.define({...})and usedeclarativeTemplate()for declarative
templates. If code explicitly observes the Promise returned bydefine(),
it resolves after the matching<f-template>supplies the concrete template.typescript// Before import { RenderableFASTElement } from "@microsoft/fast-html"; RenderableFASTElement(MyComponent).defineAsync({ name: "my-component", templateOptions: "defer-and-hydrate", }); // After MyComponent.define({ name: "my-component", template: declarativeTemplate(), });Remove
prepare()methods. Move any initialization logic toconnectedCallback:typescript// 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(); } }Remove
defer-hydrationandneeds-hydrationattributes from server-rendered markup:html<my-component defer-hydration needs-hydration text="Hello"> <template shadowrootmode="open">...</template> </my-component> <my-component text="Hello"> <template shadowrootmode="open">...</template> </my-component>Await
$fastController.isPrerenderedto detect prerendered components:typescriptconnectedCallback() { super.connectedCallback(); this.$fastController.isPrerendered.then(prerendered => { if (!prerendered) { this.fetchData(); } }); }
Modularize Schema, ObserverMap, and AttributeMap
Import changes
Configuration types have moved from template.ts to their owning modules. If you import types directly from internal paths, update your imports:
| Before | After |
|---|---|
import type { ObserverMapConfig } from "./template.js" |
import type { ObserverMapConfig } from "./observer-map.js" |
import type { AttributeMapConfig } from "./template.js" |
import type { AttributeMapConfig } from "./attribute-map.js" |
Public declarative imports now come from@microsoft/fast-element/declarative.js rather than@microsoft/fast-html, and declarative utilities come from@microsoft/fast-element/declarative-utilities.js. Map extensions come from@microsoft/fast-element/attribute-map.js and@microsoft/fast-element/observer-map.js.
Schema changes
Schema.jsonSchemaMap (static property) has been replaced by:
- An instance-level
schemaMapon eachSchemainstance (private) - A module-level
schemaRegistryexport for cross-element lookups
| Before | After |
|---|---|
Schema.jsonSchemaMap.get('my-element') |
import { schemaRegistry } from "@microsoft/fast-element"; schemaRegistry.get('my-element') |
Public exports
The public entrypoint exports the functional declarative API:
| Export | Purpose |
|---|---|
declarativeTemplate() |
Resolves <f-template> markup for a FAST element definition |
attributeMap() |
Definition extension for automatic @attr property registration |
observerMap() |
Definition extension for automatic deep observation |
Schema |
JSON schema builder class |
schemaRegistry |
Module-level registry for cross-element schema lookups |
JSONSchema |
JSON Schema type interface |
CachedPathMap |
Schema registry map type |
Simplified ObserverMap and AttributeMap defaults
The explicit ObserverMapOption.all and AttributeMapOption.all constants have
been removed. Calling observerMap() with no arguments observes every
discovered root property, and calling attributeMap() with no arguments maps
every discovered leaf binding.
| Before | After |
|---|---|
observerMap(ObserverMapOption.all) |
observerMap() |
attributeMap(AttributeMapOption.all) |
attributeMap() |
Attribute name strategy default changed
The default attribute-name-strategy for declarative attribute mapping is now"camelCase" instead of "none". A binding key such as {{firstName}} maps to
the firstName property and the first-name HTML attribute by default. If your
templates depended on literal attribute names, opt back in to the old behavior:
import { attributeMap } from "@microsoft/fast-element/attribute-map.js";
MyElement.define(
{
name: "my-element",
template: declarativeTemplate(),
},
[attributeMap({ "attribute-name-strategy": "none" })],
);When using @microsoft/fast-build, keep the server and client settings aligned:
fast build --attribute-name-strategy=noneDeclarative TemplateElement API removal
The public declarative API is now the functional API. The <f-template>
implementation is internal and is defined automatically by declarativeTemplate().
| Removed | Replacement |
|---|---|
TemplateElement public export |
declarativeTemplate() on each FAST element definition |
TemplateElement.define({ name: "f-template" }) |
No manual definition; declarativeTemplate() defines the internal publisher in the target registry |
TemplateElement.config(callbacks) / HydrationLifecycleCallbacks |
enableHydration().whenHydrated(tagName) for tag-specific hydration waits and enableHydration().whenHydrated() for the active hydration batch |
TemplateElement.options({ "my-el": { attributeMap, observerMap } }) |
Define extensions: MyElement.define(definition, [attributeMap(...), observerMap(...)]) |
ElementOptions / ElementOptionsDictionary |
No replacement |
AttributeMap / ObserverMap class exports from the old declarative public surface |
attributeMap() / observerMap() extension helpers and their config types |
Hydration is also no longer installed by @microsoft/fast-element.
Call enableHydration() from @microsoft/fast-element/hydration.js before FAST
elements connect when prerendered Declarative Shadow DOM should be reused.
Schema-driven maps and optional definition schema
attributeMap() and observerMap() are now schema-driven extensions that are
factored away from declarative templating. Import them from@microsoft/fast-element for declarative or non-declarative use:
import { attributeMap } from "@microsoft/fast-element/attribute-map.js";
import { observerMap } from "@microsoft/fast-element/observer-map.js";FASTElementDefinition.schema is optional. declarativeTemplate() assigns it
automatically when it parses <f-template> markup. Manual schema users can pass
a schema in the element definition, and observerMap() can also take a schema
directly in configuration:
import { FASTElement, Schema } from "@microsoft/fast-element";
import { observerMap } from "@microsoft/fast-element/observer-map.js";
class MyElement extends FASTElement {}
const schema = new Schema("my-element");
schema.addPath({
rootPropertyName: "user",
pathConfig: {
type: "default",
parentContext: null,
currentContext: null,
path: "user.name",
},
childrenMap: null,
});
MyElement.define({ name: "my-element" }, [observerMap({ schema })]);Packages Fast Element Docs Styles Api Report.Api (`packages/fast-element/docs/styles/api-report.api.md`)
API Report File for "@microsoft/fast-element"
Do not edit this file. It is a report generated by API Extractor.
/* Detailed source-code truncated for AI context efficiency. */Sites Website Src Docs 1.X Apps And Experiences (`sites/website/src/docs/1.x/apps-and-experiences.md`)
layout: 1x eleventyNavigation: key: apps-and-experiences1x title: Apps and Experiences navigationOptions: activeKey: apps-and-experiences1x permalink: false
Sites Website Src Docs 1.X Community (`sites/website/src/docs/1.x/community.md`)
layout: 1x eleventyNavigation: key: community1x title: Community navigationOptions: activeKey: community1x permalink: false
Sites Website Src Docs 1.X Components (`sites/website/src/docs/1.x/components.md`)
layout: 1x eleventyNavigation: key: components1x title: Components navigationOptions: activeKey: components1x permalink: false
Sites Website Src Docs 1.X Design Systems (`sites/website/src/docs/1.x/design-systems.md`)
layout: 1x eleventyNavigation: key: design-systems1x title: Design Systems navigationOptions: activeKey: design-systems1x permalink: false
Sites Website Src Docs 1.X Design (`sites/website/src/docs/1.x/design.md`)
layout: 1x eleventyNavigation: key: design1x title: Design navigationOptions: activeKey: design1x permalink: false
Sites Website Src Docs 1.X Fast Element (`sites/website/src/docs/1.x/fast-element.md`)
layout: 1x eleventyNavigation: key: fast-element1x title: FAST Element navigationOptions: activeKey: fast-element1x permalink: false
Sites Website Src Docs 1.X Integrations (`sites/website/src/docs/1.x/integrations.md`)
layout: 1x eleventyNavigation: key: integrations1x title: Integrations navigationOptions: activeKey: integrations1x permalink: false
Sites Website Src Docs 1.X Introduction (`sites/website/src/docs/1.x/introduction.md`)
id: introduction title: Introduction eleventyNavigation: key: introduction1x title: Introduction navigationOptions: activeKey: introduction1x layout: 1x
Where should I start?
Pick the statement you most identify with, and follow the links where they lead. Come back and explore another topic at any time. Or, click here to learn more about FAST.
- "I just want ready-made components!"
- "I want to build my own design system."
- "I want to build my own components."
- "I need to integrate FAST with another framework or build system."
- "I want to look at a quick reference."
- "I want to contribute to FAST."
What is FAST?
FAST is a collection of technologies built on Web Components and modern Web Standards, designed to help you efficiently tackle some of the most common challenges in website and application design and development.
What are Web Components?
"Web Components" is an umbrella term that refers to a collection of web standards focused on enabling the creation of custom HTML elements. Some of the standards that are under the umbrella include the ability to define new HTML tags, plug into a standard component lifecycle, encapsulate HTML rendering and CSS, parameterize CSS, skin components, and more. Each of these platform features is defined by the W3C and has shipped in every major browser today.
How does FAST leverage Web Components?
One of FAST's driving principals is "strive to adopt open, web standards-based approaches as much as possible." To that end, FAST is built directly on the W3C Web Component standards mentioned above, and does not create its own component model. This allows components built with FAST to function the same as built-in, native HTML elements. You do not need a framework to use FAST components, but you can use them in combination with any framework or library of your choice.
How can FAST help me?
To understand how FAST can help you, let's take a look at the FAST tech stack from top to bottom.
At the top of the stack, FAST provides a set of Web Components: @microsoft/fast-components. This library includes a common set of components found in many websites and apps.
flowchart
subgraph FAST [ ]
direction TB
FastComponents("@microsoft/fast-components\n(Component library that implements FAST Frame Design System)")
end
style FAST fill:transparent,stroke:none
style FastComponents fill:#f4f4f4,stroke:#fb356d,stroke-width:3px,color:#333Example components include button, card, modal, menu, tab, tree-view, and more. @microsoft/fast-components provides an industry-focused design system, which we call "FAST Frame". If you're looking to integrate FAST components into an existing site or app, or if you need more control over the theme of the components, this is the option you'll want to start with.
What if you're not just looking for a set of components to use, but you also need to implement a custom design system?
This is where the second level of the stack comes into play. @microsoft/fast-foundation provides foundational building blocks that can be assembled to create new design systems and component libraries.
flowchart
subgraph FAST [ ]
direction TB
FastComponents("@microsoft/fast-components\n(Component library that implements FAST Frame Design System)")
FastFoundation("@microsoft/fast-foundation\n(Building blocks for custom design systems/component libraries)")
FastComponents -.-> FastFoundation
end
style FAST fill:transparent,stroke:none
style FastComponents fill:#f4f4f4,stroke:#f4f4f4,color:#333
style FastFoundation fill:#f4f4f4,stroke:#fb356d,stroke-width:3px,color:#333For example, if you wanted to implement Google's Material Design, you could do that using @microsoft/fast-foundation; you could also implement something like Twitter Bootstrap. @microsoft/fast-components assemble the building blocks of @microsoft/fast-foundation to create its component sets.
What types of building blocks are we talking about though?
Perhaps the most valuable feature of the foundation is that it provides base component behaviors and templates for the standard components. So, if you want to implement a tree-view in your design system, for example, you would use the foundation base component behavior and template, but combine it with your own styles.
The foundation components implement the state management, accessibility, keyboard navigation, and extensibility/composition model so you don't have to write that code. Additionally, foundation provides facilities for dynamic style behaviors, CSS property management, algorithmic color, RTL, high contrast, and more. You don't have to write any of that. Just assemble the building blocks and add your styles to create your own component library, expressing your own design system. So far we've talked about using existing components and creating new design systems and component libraries from existing pieces. But FAST enables you to create completely new Web Components as well.
Enter @microsoft/fast-element, the lowest level part of the FAST tech stack. This is a lightweight library for building performant, memory-efficient, standards-compliant Web Components.
flowchart
subgraph FAST [ ]
direction TB
FastComponents("@microsoft/fast-components\n(Component library that implements FAST Frame Design System)")
FastFoundation("@microsoft/fast-foundation\n(Building blocks for custom design systems/component libraries)")
FastElement("@microsoft/fast-element\n(Lightweight library for building custom Web Components)")
FastComponents -.-> FastFoundation -.-> FastElement
end
style FAST fill:transparent,stroke:none
style FastComponents fill:#f4f4f4,stroke:#f4f4f4,color:#333
style FastFoundation fill:#f4f4f4,stroke:#f4f4f4,color:#333
style FastElement fill:#f4f4f4,stroke:#fb356d,stroke-width:3px,color:#333@microsoft/fast-element provides a thin layer of opinion on top of Web Components, lifting the level of abstraction just enough to make it easier and faster to build components. @microsoft/fast-element helps by providing attribute/property syncing, rich Model-View-ViewModel (MVVM), efficient template rendering/update, style composition, and much more. The entire @microsoft/fast-element library, without tree-shaking, is around 10kb minified and GZipped. It was designed for tree-shaking from the beginning, so any feature you don't use when building a component will be removed during build, allowing for highly efficient optimized payloads as small as 4.5k.
Joining the community
Looking to get answers to questions or engage with us in realtime? Our community is most active on Discord. Submit requests and issues on GitHub, or join us by contributing on some good first issues via GitHub.
We look forward to building an amazing open source community with you!
Sites Website Src Docs 1.X Resources (`sites/website/src/docs/1.x/resources.md`)
layout: 1x eleventyNavigation: key: resources1x title: Resources navigationOptions: activeKey: resources1x permalink: false
Sites Website Src Docs 1.X Tools (`sites/website/src/docs/1.x/tools.md`)
layout: 1x eleventyNavigation: key: tools1x title: Tools navigationOptions: activeKey: tools1x permalink: false
Sites Website Src Docs 2.X Advanced (`sites/website/src/docs/2.x/advanced.md`)
layout: 2x eleventyNavigation: key: advanced2x title: Advanced Concepts order: 3 navigationOptions: activeKey: advanced2x permalink: false
Sites Website Src Docs 2.X Api (`sites/website/src/docs/2.x/api.md`)
layout: 2x eleventyNavigation: key: api2x title: API Reference order: 4 navigationOptions: activeKey: api2x permalink: false
Sites Website Src Docs 2.X Community (`sites/website/src/docs/2.x/community.md`)
layout: 2x eleventyNavigation: key: community2x title: Community Contribution navigationOptions: activeKey: community2x permalink: false
Sites Website Src Docs 2.X Integrations (`sites/website/src/docs/2.x/integrations.md`)
id: integrations
title: Integrations
layout: 2x
eleventyNavigation:
key: integrations2x
title: Integrations
navigationOptions:
activeKey: integrations2x
keywords:
- integrations
- angular
- asp.net
- aurelia 2
- ember
- react
- rollup
- svelte
- vite
- vue
- webpack
- web components
Integrations
If your project includes one of the popular frameworks below, we've included some helpful tips.
Natively supported, no additional configuration needed:
- ASP.NET
- Ember
- Svelte
- Vite
- Vue
- Webpack
Angular
Angular works well with web components,
To work with typings within Angular, check out their documentation on custom typings.
Aurelia 2
FAST works flawlessly with Aurelia 2, with full integration into the binding engine and component model.
Enabling two-way bindings
Aurelia knows by default how to listen for changes in native elements. Now we need to teach it how to listen for changes in FAST elements. You can do so by extending its templating syntax.
You can either use a wrapper developed by the community or teach Aurelia manually:
Import and register `aurelia-fast-adapter`
Start by installing the adapter
npm install aurelia-fast-adapterand then simply register it from your src/main.ts:
// src/main.ts
import { FASTAdapter } from 'aurelia-fast-adapter';
Aurelia
.register(FASTAdapter) // add this line
// other registrations...
.start();If you use FAST in its default configuration that's all you need to do. But if you changed the prefix of your components to something else, you can customize the adapter as such:
// src/main.ts
import { FASTAdapter } from 'aurelia-fast-adapter';
Aurelia
.register(FASTAdapter.customize({withPrefix: 'my-custom-prefix'}) // customized with prefix
.start();Also, in case you have local components that require two-way binding, you can adjust the adapter before to register it as such:
// src/main.ts
import { FASTAdapter } from 'aurelia-fast-adapter';
// this line will tell the adapter that it must use two-way binding on the <my-custom-prefix-date-field> component and use this two-way binding on the `value` property. It's possible to add several properties at once if necessary
FASTAdapter.tags['DATE-FIELD'] = ['value'];
Aurelia
.register(FASTAdapter.customize({withPrefix: 'my-custom-prefix'})
.start();Congratulations! You're now set up to use FAST and Aurelia 2!
Manually teach Aurelia 2 about two-way binding:
If the example doesn't seem obvious, the following prerequisite reads are recommended:
The following is a code example of how to teach Aurelia to work seamlessly with Microsoft FAST.
/* Detailed source-code truncated for AI context efficiency. */React
See the React documentation on including custom elements.
Rollup
In your rollup.config.js file in the root of your project folder can look something like this:
import transformTaggedTemplate from 'rollup-plugin-transform-tagged-template';
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import cleaner from 'rollup-plugin-cleaner';
import copy from 'rollup-plugin-copy';
import serve from 'rollup-plugin-serve';
import terser from '@rollup/plugin-terser';
export default {
input: 'src/main.ts',
output: {
file: 'dist/bundle.js',
name: 'bundle',
format: 'umd',
sourcemap: true
},
plugins: [
transformTaggedTemplate({
tagsToProcess: ['html','css'],
parserOptions: {
sourceType: "module",
plugins: [
"typescript",
[
"decorators",
{ decoratorsBeforeExport: true }
]
]
},
transformer(data) {
data = data.replace(/\s([{}()>~+=^$:!;])\s/gm, '$1');
data = data.replace(/([",[]])\s+/gm, '$1');
data = data.replace(/\s{2,}/gm, ' ');
return data.trim();
}
}),
typescript(),
resolve(),
cleaner({
targets: [
'dist'
]
}),
copy({
targets: [
{ src: 'index.html', dest: 'dist' },
]
}),
serve({
open: true,
contentBase: 'dist'
}),
terser(),
]
};Let's go over a few of the plugins we would suggest using:
transformTaggedTemplateminifies content within tagged templates (e.g. html and css)typescriptallows us to write our source files in TypeScript.resolveallows us to import modules installed from npm, like@microsoft/fast-element.
TypeScript
Here's an example starter tsconfig.json that you can use:
{
"compilerOptions": {
"pretty": true,
"target": "ES2015",
"module": "ES2015",
"moduleResolution": "node",
"importHelpers": true, // when using decorators this allows for the smallest footprint using tslib
"experimentalDecorators": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noEmitOnError": true,
"strict": true,
"outDir": "dist/build",
"rootDir": "src",
"lib": [
"dom",
"esnext"
]
},
"include": [
"src"
]
}Note the inclusion of "dom" and "experimentalDecorators".
You can learn more about tsconfig.json options in the official TypeScript documentation.
:::note
Do not set useDefineForClassFields to true in your tsconfig.json if you are using decorators (e.g. ExperimentalDecorators). These two features conflict at present. This will be resolved in future versions of TypeScript and FAST.
:::
Sites Website Src Docs 2.X Introduction (`sites/website/src/docs/2.x/introduction.md`)
id: introduction
title: Introduction
layout: 2x
eleventyNavigation:
key: introduction2x
title: Introduction
order: 1
navigationOptions:
activeKey: introduction2x
keywords:
- introduction
- web components
Introduction
What is FAST?
FAST is a collection of technologies built on Web Components and modern Web Standards, designed to help you efficiently tackle some of the most common challenges in website and application design and development.
What are Web Components?
"Web Components" is an umbrella term that refers to a collection of web standards focused on enabling the creation of custom HTML elements. Some of the standards that are under the umbrella include the ability to define new HTML tags, plug into a standard component lifecycle, encapsulate HTML rendering and CSS, parameterize CSS, skin components, and more. Each of these platform features is defined by the W3C and has shipped in every major browser today.
How does FAST leverage Web Components?
@microsoft/fast-element provides a thin layer of opinion on top of Web Components, lifting the level of abstraction just enough to make it easier and faster to build components by providing:
- Attribute/property syncing
- Rich Model-View-ViewModel (MVVM) support
- Efficient template rendering/update
- Style composition
- Elements refs, template directives, and much more.
@microsoft/fast-element was designed for tree-shaking from the beginning, so any feature you don't use when building a component will be removed during build, allowing for highly efficient optimized payloads. See the Export Sizes page for detailed per-export bundle sizes.
One of FAST's driving principles is "strive to adopt open, web standards-based approaches as much as possible." To that end, FAST is built directly on the W3C Web Component standards mentioned above, and does not create its own component model. This allows components built with FAST to function the same as built-in, native HTML elements. You do not need a framework to use FAST components, but you can use them in combination with any framework or library of your choice.
Sites Website Src Docs 2.X Migration Guide (`sites/website/src/docs/2.x/migration-guide.md`)
id: migration-guide
title: Migration Guide
layout: 2x
eleventyNavigation:
key: migration-guide2x
title: Migration Guide
navigationOptions:
activeKey: migration-guide2x
keywords:
- migrate
- migration
- web components
Migrating from 1.x to 2.x
Breaking Changes
`cssPartial`
The cssPartial export no longer exists, in its place use css.partial.
1.x Example:
import { cssPartial } from "@microsoft/fast-element";
cssPartial`
:host {
color: red;
}`;2.x Example:
import { css } from "@microsoft/fast-element";
css.partial`
:host {
color: red;
}`;`CSSDirective`
The CSSDirective has been updated to need to be defined. Additionally it cannot be extended from and must use implements.
1.x Example:
import { CSSDirective } from "@microsoft/fast-element"
class MyCSSDirective extends CSSDirective {
createCSS() {
return "color: red;"
}
}
export const myCssDirective = new MyCSSDirective();2.x Example:
import { CSSDirective } from "@microsoft/fast-element"
class MyCSSDirective implements CSSDirective {
createCSS() {
return "color: red;"
}
}
CSSDirective.define(MyCSSDirective);
export const myCssDirective = new MyCSSDirective();`CSSDirective` with behaviors
Behaviors have been extracted from CSSDirective so that they can be used in a modular fashion. The API has also been updated to be more intuitive and re-use methods that are common for web components (see connectedCallback in the 2.x example below).
1.x Example:
import { CSSDirective } from "@microsoft/fast-element"
class MyCSSDirective extends CSSDirective {
public cssProperty: string = "background";
createCSS() {
return `display: block; color: red;`
}
createBehavior() {
var that = this;
return {
bind(el) {
el.style.setProperty(that.cssProperty, "yellow")
},
unbind(el) {
el.style.removeProperty(that.cssProperty);
}
}
}
}
export const myCssDirective = new MyCSSDirective();2.x Example:
import { css, CSSDirective, AddBehavior, HostBehavior } from "@microsoft/fast-element"
const cssProperty: string = "background";
const myCssBehavior: HostBehavior = {
connectedCallback: (controller) => {
controller.addStyles(css`:host {${cssProperty}: yellow }`)
},
disconnectedCallback: (controller) => {
controller.removeStyles(css`:host {${cssProperty}: yellow }`)
}
}
class MyCSSDirective implements CSSDirective {
createCSS(add: AddBehavior) {
add(myCssBehavior);
return `display: block; color: red;`
}
}
CSSDirective.define(MyCSSDirective);
export const myCssDirective = new MyCSSDirective();Suggested Changes
While some of the APIs remain available, we suggest going forward that certain patterns be updated.
`@customElement`
We prefer using FASTElement and its .define() method over the @customElement decorator (which is still used under the hood). We believe this more closely matches with how you define custom elements using the native customElements.define().
1.x Example:
import { FASTElement, customElement } from '@microsoft/fast-element';
@customElement('my-component')
export class MyComponent extends FASTElement {}2.x Example:
import { FASTElement } from "@microsoft/fast-element";
class MyComponent extends FASTElement {}
MyComponent.define({
name: "my-component"
});Sites Website Src Docs 2.X Resources (`sites/website/src/docs/2.x/resources.md`)
layout: 2x eleventyNavigation: key: resources2x title: Resources navigationOptions: activeKey: resources2x permalink: false
Sites Website Src Docs 3.X Advanced (`sites/website/src/docs/3.x/advanced.md`)
layout: 3x eleventyNavigation: key: advanced3x title: Advanced Concepts order: 3 navigationOptions: activeKey: advanced3x permalink: false
Sites Website Src Docs 3.X Api (`sites/website/src/docs/3.x/api.md`)
id: api-reference
title: API Reference
layout: 3x
eleventyNavigation:
key: api3x
title: API Reference
order: 4
navigationOptions:
activeKey: api3x
permalink: /docs/3.x/api/overview/
description: FAST Element 3.x API reference and package export guide.
keywords:
- API
- reference
- exports
- imports
- stability
API Reference
Use this page to choose the right FAST Element import path and then browse the
generated API reference for the public surface of each entrypoint.
Root vs. path exports
The root package export, @microsoft/fast-element, is the primary entrypoint for
FAST Element's component authoring APIs. It is the best default when you are
building custom elements with FASTElement, html, css, attr, directives,
observables, state helpers, and other core primitives.
FAST Element 3.x also publishes focused path exports. Use a path export when you
want a smaller, feature-specific entrypoint or when the API is path-only. The
path-only entrypoints should always be imported from their documented package
paths rather than from the package root.
Common import choices
| Need | Import from | Reference |
|---|---|---|
| Core component authoring APIs | @microsoft/fast-element |
Root API reference |
| Declarative HTML templates | @microsoft/fast-element/declarative.js |
Declarative API reference and Declarative HTML overview |
| Server-rendered hydration | @microsoft/fast-element/hydration.js |
Hydration flow and path export list |
| W3C Context protocol helpers | @microsoft/fast-element/context.js |
Context API reference |
| Dependency injection container APIs | @microsoft/fast-element/di.js |
DI API reference and Dependency Injection guide |
Individual core features, such as html, css, attr, or directives |
The matching path export, such as @microsoft/fast-element/html.js |
Path exports |
Stability tags
The generated API pages include release-tag callouts from API Extractor:
- Public APIs are the supported surface for production code.
- Beta APIs are preview APIs that can change based on feedback.
- Alpha APIs are early previews and should be treated as less stable than
beta APIs. - Internal APIs are not intended for application code, even when they appear
in a type signature or generated page.
Prefer public APIs when possible. If a generated page includes a beta or alpha
notice, review the callout before depending on that symbol in production code.
Generated API docs
The API reference pages are generated from FAST Element's API Extractor output
and should not be edited by hand. Start with the generated@microsoft/fast-element root reference for the
main package surface, then use the path-export references for focused APIs:
@microsoft/fast-element/declarative.js@microsoft/fast-element/context.js@microsoft/fast-element/di.js
For the complete list of supported package paths, see the
Path Exports page.
Sites Website Src Docs 3.X Community (`sites/website/src/docs/3.x/community.md`)
layout: 3x eleventyNavigation: key: community3x title: Community Contribution navigationOptions: activeKey: community3x permalink: false
Sites Website Src Docs 3.X Declarative Templates (`sites/website/src/docs/3.x/declarative-templates.md`)
layout: 3x eleventyNavigation: key: declarative-templates3x title: Declarative HTML order: 3 navigationOptions: activeKey: declarative-templates3x permalink: false
Sites Website Src Docs 3.X Integrations (`sites/website/src/docs/3.x/integrations.md`)
id: integrations
title: Integrations
layout: 3x
eleventyNavigation:
key: integrations3x
title: Integrations
navigationOptions:
activeKey: integrations3x
keywords:
- integrations
- angular
- asp.net
- aurelia 2
- ember
- react
- rollup
- svelte
- vite
- vue
- webpack
- web components
Integrations
If your project includes one of the popular frameworks below, we've included some helpful tips.
Natively supported, no additional configuration needed:
- ASP.NET
- Ember
- Svelte
- Vite
- Vue
- Webpack
Angular
Angular works well with web components,
To work with typings within Angular, check out their documentation on custom typings.
Aurelia 2
FAST works flawlessly with Aurelia 2, with full integration into the binding engine and component model.
Enabling two-way bindings
Aurelia knows by default how to listen for changes in native elements. Now we need to teach it how to listen for changes in FAST elements. You can do so by extending its templating syntax.
You can either use a wrapper developed by the community or teach Aurelia manually:
Import and register `aurelia-fast-adapter`
Start by installing the adapter
npm install aurelia-fast-adapterand then simply register it from your src/main.ts:
// src/main.ts
import { FASTAdapter } from 'aurelia-fast-adapter';
Aurelia
.register(FASTAdapter) // add this line
// other registrations...
.start();If you use FAST in its default configuration that's all you need to do. But if you changed the prefix of your components to something else, you can customize the adapter as such:
// src/main.ts
import { FASTAdapter } from 'aurelia-fast-adapter';
Aurelia
.register(FASTAdapter.customize({withPrefix: 'my-custom-prefix'}) // customized with prefix
.start();Also, in case you have local components that require two-way binding, you can adjust the adapter before to register it as such:
// src/main.ts
import { FASTAdapter } from 'aurelia-fast-adapter';
// this line will tell the adapter that it must use two-way binding on the <my-custom-prefix-date-field> component and use this two-way binding on the `value` property. It's possible to add several properties at once if necessary
FASTAdapter.tags['DATE-FIELD'] = ['value'];
Aurelia
.register(FASTAdapter.customize({withPrefix: 'my-custom-prefix'})
.start();Congratulations! You're now set up to use FAST and Aurelia 2!
Manually teach Aurelia 2 about two-way binding:
If the example doesn't seem obvious, the following prerequisite reads are recommended:
The following is a code example of how to teach Aurelia to work seamlessly with Microsoft FAST.
/* Detailed source-code truncated for AI context efficiency. */React
See the React documentation on including custom elements.
Rollup
In your rollup.config.js file in the root of your project folder can look something like this:
import transformTaggedTemplate from 'rollup-plugin-transform-tagged-template';
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import cleaner from 'rollup-plugin-cleaner';
import copy from 'rollup-plugin-copy';
import serve from 'rollup-plugin-serve';
import terser from '@rollup/plugin-terser';
export default {
input: 'src/main.ts',
output: {
file: 'dist/bundle.js',
name: 'bundle',
format: 'umd',
sourcemap: true
},
plugins: [
transformTaggedTemplate({
tagsToProcess: ['html','css'],
parserOptions: {
sourceType: "module",
plugins: [
"typescript",
[
"decorators",
{ decoratorsBeforeExport: true }
]
]
},
transformer(data) {
data = data.replace(/\s([{}()>~+=^$:!;])\s/gm, '$1');
data = data.replace(/([",[]])\s+/gm, '$1');
data = data.replace(/\s{2,}/gm, ' ');
return data.trim();
}
}),
typescript(),
resolve(),
cleaner({
targets: [
'dist'
]
}),
copy({
targets: [
{ src: 'index.html', dest: 'dist' },
]
}),
serve({
open: true,
contentBase: 'dist'
}),
terser(),
]
};Let's go over a few of the plugins we would suggest using:
transformTaggedTemplateminifies content within tagged templates (e.g. html and css)typescriptallows us to write our source files in TypeScript.resolveallows us to import modules installed from npm, like@microsoft/fast-element.
TypeScript
Here's an example starter tsconfig.json that you can use:
{
"compilerOptions": {
"pretty": true,
"target": "ES2015",
"module": "ES2015",
"moduleResolution": "node",
"importHelpers": true, // when using decorators this allows for the smallest footprint using tslib
"experimentalDecorators": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noEmitOnError": true,
"strict": true,
"outDir": "dist/build",
"rootDir": "src",
"lib": [
"dom",
"esnext"
]
},
"include": [
"src"
]
}Note the inclusion of "dom" and "experimentalDecorators".
You can learn more about tsconfig.json options in the official TypeScript documentation.
:::note
Do not set useDefineForClassFields to true in your tsconfig.json if you are using decorators (e.g. ExperimentalDecorators). These two features conflict at present. This will be resolved in future versions of TypeScript and FAST.
:::
Sites Website Src Docs 3.X Introduction (`sites/website/src/docs/3.x/introduction.md`)
id: introduction
title: Introduction
layout: 3x
eleventyNavigation:
key: introduction3x
title: Introduction
order: 1
navigationOptions:
activeKey: introduction3x
keywords:
- introduction
- web components
Introduction
What is FAST?
FAST is a collection of technologies built on Web Components and modern Web Standards, designed to help you efficiently tackle some of the most common challenges in website and application design and development.
What are Web Components?
"Web Components" is an umbrella term that refers to a collection of web standards focused on enabling the creation of custom HTML elements. Some of the standards that are under the umbrella include the ability to define new HTML tags, plug into a standard component lifecycle, encapsulate HTML rendering and CSS, parameterize CSS, skin components, and more. Each of these platform features is defined by the W3C and has shipped in every major browser today.
How does FAST leverage Web Components?
@microsoft/fast-element provides a thin layer of opinion on top of Web Components, lifting the level of abstraction just enough to make it easier and faster to build components by providing:
- Attribute/property syncing
- Rich Model-View-ViewModel (MVVM) support
- Efficient template rendering/update
- Style composition
- Elements refs, template directives, and much more.
The entire @microsoft/fast-element library, without tree-shaking, is around 10kb minified and GZipped. It was designed for tree-shaking from the beginning, so any feature you don't use when building a component will be removed during build, allowing for highly efficient optimized payloads as small as 4.5k.
One of FAST's driving principles is "strive to adopt open, web standards-based approaches as much as possible." To that end, FAST is built directly on the W3C Web Component standards mentioned above, and does not create its own component model. This allows components built with FAST to function the same as built-in, native HTML elements. You do not need a framework to use FAST components, but you can use them in combination with any framework or library of your choice.
Sites Website Src Docs 3.X Migration Guide (`sites/website/src/docs/3.x/migration-guide.md`)
layout: 3x eleventyNavigation: key: migration-guide3x title: Migration Guide order: 8 navigationOptions: activeKey: migration-guide3x permalink: false
Sites Website Src Docs 3.X Resources (`sites/website/src/docs/3.x/resources.md`)
layout: 3x eleventyNavigation: key: resources3x title: Resources navigationOptions: activeKey: resources3x permalink: false
Sites Website Src Docs 1.X Api Fast Animation.Animate. Constructor (`sites/website/src/docs/1.x/api/fast-animation.animate._constructor_.md`)
id: fast-animation.animate._constructor_ title: Animate.(constructor) hide_title: true layout: 1x
@microsoft/fast-animation > Animate > (constructor)
Animate.(constructor)
Constructs a new instance of the Animate class
Signature:
constructor(element: HTMLElement, options?: AnimateConfig, effectTiming?: EffectTiming);Parameters
| Parameter | Type | Description |
|---|---|---|
| element | HTMLElement | |
| options | AnimateConfig | |
| effectTiming | EffectTiming |
Sites Website Src Docs 1.X Api Fast Animation.Animate.Addkeyframes (`sites/website/src/docs/1.x/api/fast-animation.animate.addkeyframes.md`)
id: fast-animation.animate.addkeyframes title: Animate.addKeyframes property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > addKeyframes
Animate.addKeyframes property
adds a set of keyframes to set of animation keyframes the animation should execute
Signature:
addKeyframes: (keyframes: Array>) => void;Sites Website Src Docs 1.X Api Fast Animation.Animate.Cancel (`sites/website/src/docs/1.x/api/fast-animation.animate.cancel.md`)
id: fast-animation.animate.cancel title: Animate.cancel property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > cancel
Animate.cancel property
cancels the animation
Signature:
cancel: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animate.Effecttiming (`sites/website/src/docs/1.x/api/fast-animation.animate.effecttiming.md`)
id: fast-animation.animate.effecttiming title: Animate.effectTiming property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > effectTiming
Animate.effectTiming property
Stores animation timing functions
Signature:
effectTiming: EffectTiming;Sites Website Src Docs 1.X Api Fast Animation.Animate.Finish (`sites/website/src/docs/1.x/api/fast-animation.animate.finish.md`)
id: fast-animation.animate.finish title: Animate.finish property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > finish
Animate.finish property
finishes the animation
Signature:
finish: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animate.Keyframeeffect (`sites/website/src/docs/1.x/api/fast-animation.animate.keyframeeffect.md`)
id: fast-animation.animate.keyframeeffect title: Animate.keyframeEffect property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > keyframeEffect
Animate.keyframeEffect property
Returns the key frame effect object
Signature:
get keyframeEffect(): KeyframeEffect;Sites Website Src Docs 1.X Api Fast Animation.Animate.Keyframes (`sites/website/src/docs/1.x/api/fast-animation.animate.keyframes.md`)
id: fast-animation.animate.keyframes title: Animate.keyframes property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > keyframes
Animate.keyframes property
Returns the animation's keyframes
Signature:
get keyframes(): Keyframe[];Sites Website Src Docs 1.X Api Fast Animation.Animate (`sites/website/src/docs/1.x/api/fast-animation.animate.md`)
id: fast-animation.animate title: Animate class hide_title: true layout: 1x
@microsoft/fast-animation > Animate
Animate class
Base animate type. This is extended by AnimateTo and AnimateFrom.
Signature:
export default abstract class AnimateConstructors
| Constructor | Modifiers | Description |
|---|---|---|
| (constructor)(element, options, effectTiming) | Constructs a new instance of the Animate class |
Properties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| addKeyframes | (keyframes: Array<Partial<Keyframe>>) => void | adds a set of keyframes to set of animation keyframes the animation should execute | |
| cancel | () => void | cancels the animation | |
| effectTiming | EffectTiming | Stores animation timing functions | |
| finish | () => void | finishes the animation | |
| keyframeEffect | KeyframeEffect | Returns the key frame effect object | |
| keyframes | Keyframe[] | Returns the animation's keyframes | |
| mode | AnimationMode | Tracks if the animation should animate toward an elements natural position or away from it | |
| onCancel | () => void | Callback to call when the animation is canceled | |
| onFinish | () => void | ||
| options | AnimateConfig | Stores animation options | |
| pause | () => void | pauses the animation | |
| play | () => void | plays the animation | |
| reverse | () => void | reverses an animation |
Sites Website Src Docs 1.X Api Fast Animation.Animate.Mode (`sites/website/src/docs/1.x/api/fast-animation.animate.mode.md`)
id: fast-animation.animate.mode title: Animate.mode property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > mode
Animate.mode property
Tracks if the animation should animate toward an elements natural position or away from it
Signature:
protected mode: AnimationMode;Sites Website Src Docs 1.X Api Fast Animation.Animate.Oncancel (`sites/website/src/docs/1.x/api/fast-animation.animate.oncancel.md`)
id: fast-animation.animate.oncancel title: Animate.onCancel property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > onCancel
Animate.onCancel property
Callback to call when the animation is canceled
Signature:
onCancel: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animate.Onfinish (`sites/website/src/docs/1.x/api/fast-animation.animate.onfinish.md`)
id: fast-animation.animate.onfinish title: Animate.onFinish property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > onFinish
Animate.onFinish property
Signature:
get onFinish(): () => void;
set onFinish(callback: () => void);Sites Website Src Docs 1.X Api Fast Animation.Animate.Options (`sites/website/src/docs/1.x/api/fast-animation.animate.options.md`)
id: fast-animation.animate.options title: Animate.options property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > options
Animate.options property
Stores animation options
Signature:
options: AnimateConfig;Sites Website Src Docs 1.X Api Fast Animation.Animate.Pause (`sites/website/src/docs/1.x/api/fast-animation.animate.pause.md`)
id: fast-animation.animate.pause title: Animate.pause property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > pause
Animate.pause property
pauses the animation
Signature:
pause: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animate.Play (`sites/website/src/docs/1.x/api/fast-animation.animate.play.md`)
id: fast-animation.animate.play title: Animate.play property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > play
Animate.play property
plays the animation
Signature:
play: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animate.Reverse (`sites/website/src/docs/1.x/api/fast-animation.animate.reverse.md`)
id: fast-animation.animate.reverse title: Animate.reverse property hide_title: true layout: 1x
@microsoft/fast-animation > Animate > reverse
Animate.reverse property
reverses an animation
Signature:
reverse: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animatefrom (`sites/website/src/docs/1.x/api/fast-animation.animatefrom.md`)
id: fast-animation.animatefrom title: AnimateFrom class hide_title: true layout: 1x
@microsoft/fast-animation > AnimateFrom
AnimateFrom class
An animation from provided property values to the element's current values. Extends Animate.
Signature:
export default class AnimateFrom extends AnimateProperties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| mode | AnimationMode |
Sites Website Src Docs 1.X Api Fast Animation.Animatefrom.Mode (`sites/website/src/docs/1.x/api/fast-animation.animatefrom.mode.md`)
id: fast-animation.animatefrom.mode title: AnimateFrom.mode property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateFrom > mode
AnimateFrom.mode property
Signature:
protected mode: AnimationMode;Sites Website Src Docs 1.X Api Fast Animation.Animategroup. Constructor (`sites/website/src/docs/1.x/api/fast-animation.animategroup._constructor_.md`)
id: fast-animation.animategroup._constructor_ title: AnimateGroup.(constructor) hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup > (constructor)
AnimateGroup.(constructor)
Constructs a new instance of the AnimateGroup class
Signature:
constructor(animations: Array<AnimateTo | AnimateFrom>);Parameters
| Parameter | Type | Description |
|---|---|---|
| animations | Array<AnimateTo | AnimateFrom> |
Sites Website Src Docs 1.X Api Fast Animation.Animategroup.Cancel (`sites/website/src/docs/1.x/api/fast-animation.animategroup.cancel.md`)
id: fast-animation.animategroup.cancel title: AnimateGroup.cancel property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup > cancel
AnimateGroup.cancel property
Cancels all animations in the group
Signature:
cancel: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animategroup.Finish (`sites/website/src/docs/1.x/api/fast-animation.animategroup.finish.md`)
id: fast-animation.animategroup.finish title: AnimateGroup.finish property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup > finish
AnimateGroup.finish property
Finishes all animations in the group
Signature:
finish: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animategroup (`sites/website/src/docs/1.x/api/fast-animation.animategroup.md`)
id: fast-animation.animategroup title: AnimateGroup class hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup
AnimateGroup class
Groups AnimateTo and AnimateFrom instances, providing a single API to operate on all of them.
Signature:
declare class AnimateGroupConstructors
| Constructor | Modifiers | Description |
|---|---|---|
| (constructor)(animations) | Constructs a new instance of the AnimateGroup class |
Properties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| cancel | () => void | Cancels all animations in the group | |
| finish | () => void | Finishes all animations in the group | |
| onFinish | () => void | The onFinish callback. | |
| pause | () => void | Pauses all animations in the group |
Methods
| Method | Modifiers | Description |
|---|---|---|
| play() | Play the group of animations | |
| reverse() | Reverses all animations in the group |
Sites Website Src Docs 1.X Api Fast Animation.Animategroup.Onfinish (`sites/website/src/docs/1.x/api/fast-animation.animategroup.onfinish.md`)
id: fast-animation.animategroup.onfinish title: AnimateGroup.onFinish property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup > onFinish
AnimateGroup.onFinish property
The onFinish callback.
Signature:
get onFinish(): () => void;
set onFinish(callback: () => void);Sites Website Src Docs 1.X Api Fast Animation.Animategroup.Pause (`sites/website/src/docs/1.x/api/fast-animation.animategroup.pause.md`)
id: fast-animation.animategroup.pause title: AnimateGroup.pause property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup > pause
AnimateGroup.pause property
Pauses all animations in the group
Signature:
pause: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animategroup.Play (`sites/website/src/docs/1.x/api/fast-animation.animategroup.play.md`)
id: fast-animation.animategroup.play title: AnimateGroup.play() method hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup > play
AnimateGroup.play() method
Play the group of animations
Signature:
play(): void;Returns:
void
Sites Website Src Docs 1.X Api Fast Animation.Animategroup.Reverse (`sites/website/src/docs/1.x/api/fast-animation.animategroup.reverse.md`)
id: fast-animation.animategroup.reverse title: AnimateGroup.reverse() method hide_title: true layout: 1x
@microsoft/fast-animation > AnimateGroup > reverse
AnimateGroup.reverse() method
Reverses all animations in the group
Signature:
reverse(): void;Returns:
void
Sites Website Src Docs 1.X Api Fast Animation.Animatesequence. Constructor (`sites/website/src/docs/1.x/api/fast-animation.animatesequence._constructor_.md`)
id: fast-animation.animatesequence._constructor_ title: AnimateSequence.(constructor) hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence > (constructor)
AnimateSequence.(constructor)
Constructs a new instance of the AnimateSequence class
Signature:
constructor(animations: Array<AnimateTo | AnimateFrom>);Parameters
| Parameter | Type | Description |
|---|---|---|
| animations | Array<AnimateTo | AnimateFrom> |
Sites Website Src Docs 1.X Api Fast Animation.Animatesequence.Cancel (`sites/website/src/docs/1.x/api/fast-animation.animatesequence.cancel.md`)
id: fast-animation.animatesequence.cancel title: AnimateSequence.cancel property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence > cancel
AnimateSequence.cancel property
Cancels all animations in the sequence
Signature:
cancel: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animatesequence.Finish (`sites/website/src/docs/1.x/api/fast-animation.animatesequence.finish.md`)
id: fast-animation.animatesequence.finish title: AnimateSequence.finish property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence > finish
AnimateSequence.finish property
Finishes all animations in the sequence
Signature:
finish: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animatesequence (`sites/website/src/docs/1.x/api/fast-animation.animatesequence.md`)
id: fast-animation.animatesequence title: AnimateSequence class hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence
AnimateSequence class
Animate a collection of AnimateTo and AnimateFrom in sequence.
Signature:
declare class AnimateSequenceConstructors
| Constructor | Modifiers | Description |
|---|---|---|
| (constructor)(animations) | Constructs a new instance of the AnimateSequence class |
Properties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| cancel | () => void | Cancels all animations in the sequence | |
| finish | () => void | Finishes all animations in the sequence | |
| onFinish | () => void | onFinish callback method | |
| pause | () => void | Pauses all animations in the sequence | |
| play | () => void | Play the sequence of animations | |
| reverse | () => void | Play the sequence in reverse |
Sites Website Src Docs 1.X Api Fast Animation.Animatesequence.Onfinish (`sites/website/src/docs/1.x/api/fast-animation.animatesequence.onfinish.md`)
id: fast-animation.animatesequence.onfinish title: AnimateSequence.onFinish property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence > onFinish
AnimateSequence.onFinish property
onFinish callback method
Signature:
onFinish: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animatesequence.Pause (`sites/website/src/docs/1.x/api/fast-animation.animatesequence.pause.md`)
id: fast-animation.animatesequence.pause title: AnimateSequence.pause property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence > pause
AnimateSequence.pause property
Pauses all animations in the sequence
Signature:
pause: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animatesequence.Play (`sites/website/src/docs/1.x/api/fast-animation.animatesequence.play.md`)
id: fast-animation.animatesequence.play title: AnimateSequence.play property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence > play
AnimateSequence.play property
Play the sequence of animations
Signature:
play: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animatesequence.Reverse (`sites/website/src/docs/1.x/api/fast-animation.animatesequence.reverse.md`)
id: fast-animation.animatesequence.reverse title: AnimateSequence.reverse property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateSequence > reverse
AnimateSequence.reverse property
Play the sequence in reverse
Signature:
reverse: () => void;Sites Website Src Docs 1.X Api Fast Animation.Animateto (`sites/website/src/docs/1.x/api/fast-animation.animateto.md`)
id: fast-animation.animateto title: AnimateTo class hide_title: true layout: 1x
@microsoft/fast-animation > AnimateTo
AnimateTo class
An animation to provided property values from the element's current values. Extends Animate.
Signature:
export default class AnimateTo extends AnimateProperties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| mode | AnimationMode |
Sites Website Src Docs 1.X Api Fast Animation.Animateto.Mode (`sites/website/src/docs/1.x/api/fast-animation.animateto.mode.md`)
id: fast-animation.animateto.mode title: AnimateTo.mode property hide_title: true layout: 1x
@microsoft/fast-animation > AnimateTo > mode
AnimateTo.mode property
Signature:
protected mode: AnimationMode;Sites Website Src Docs 1.X Api Fast Animation.Cubicbezier (`sites/website/src/docs/1.x/api/fast-animation.cubicbezier.md`)
id: fast-animation.cubicbezier title: cubicBezier() function hide_title: true layout: 1x
@microsoft/fast-animation > cubicBezier
cubicBezier() function
Get a cubic bezier curve, formatted as a string, by name.
Signature:
export declare function cubicBezier(name: "linear" | "easeOut" | "easeOutSmooth" | "easeIn" | "drillIn" | "backToApp" | "appToApp" | "fastIn" | "fastOut" | "fastInOut" | "exponential" | "fastInFortySevenPercent" | "exponentialReversed" | "navPane" | /* @deprecated */ string): string;Parameters
| Parameter | Type | Description |
|---|---|---|
| name | "linear" | "easeOut" | "easeOutSmooth" | "easeIn" | "drillIn" | "backToApp" | "appToApp" | "fastIn" | "fastOut" | "fastInOut" | "exponential" | "fastInFortySevenPercent" | "exponentialReversed" | "navPane" | /* @deprecated */ string | the name of the bezier curve to use. |
Returns:
string
Sites Website Src Docs 1.X Api Fast Animation.Fadein (`sites/website/src/docs/1.x/api/fast-animation.fadein.md`)
id: fast-animation.fadein title: fadeIn() function hide_title: true layout: 1x
@microsoft/fast-animation > fadeIn
fadeIn() function
Creates an animation to fade an element into view
Signature:
export declare function fadeIn(element: HTMLElement, effectTiming?: EffectTiming): AnimateTo;Parameters
| Parameter | Type | Description |
|---|---|---|
| element | HTMLElement | |
| effectTiming | EffectTiming |
Returns:
AnimateTo
Sites Website Src Docs 1.X Api Fast Animation.Fadeout (`sites/website/src/docs/1.x/api/fast-animation.fadeout.md`)
id: fast-animation.fadeout title: fadeOut() function hide_title: true layout: 1x
@microsoft/fast-animation > fadeOut
fadeOut() function
Creates an animation to fade an element out of view
Signature:
export declare function fadeOut(element: HTMLElement, effectTiming?: EffectTiming): AnimateTo;Parameters
| Parameter | Type | Description |
|---|---|---|
| element | HTMLElement | |
| effectTiming | EffectTiming |
Returns:
AnimateTo
Sites Website Src Docs 1.X Api Fast Animation (`sites/website/src/docs/1.x/api/fast-animation.md`)
id: fast-animation title: fast-animation hide_title: false layout: 1x
Classes
| Class | Description |
|---|---|
| Animate | Base animate type. This is extended by AnimateTo and AnimateFrom. |
| AnimateFrom | An animation from provided property values to the element's current values. Extends Animate. |
| AnimateGroup | Groups AnimateTo and AnimateFrom instances, providing a single API to operate on all of them. |
| AnimateSequence | Animate a collection of AnimateTo and AnimateFrom in sequence. |
| AnimateTo | An animation to provided property values from the element's current values. Extends Animate. |
| ScrollTrigger | Utility for registering element/callback pairs where the callback will be called on scroll while the element is in view. |
| ViewEnterTrigger | Utility for registering element/callback pairs where the callback will be called when the element enters the view-port |
| ViewExitTrigger | Utility for registering element/callback pairs where the callback will be invoked when the element exits the view-port |
Functions
| Function | Description |
|---|---|
| cubicBezier(name) | Get a cubic bezier curve, formatted as a string, by name. |
| fadeIn(element, effectTiming) | Creates an animation to fade an element into view |
| fadeOut(element, effectTiming) | Creates an animation to fade an element out of view |
Sites Website Src Docs 1.X Api Fast Animation.Scrolltrigger (`sites/website/src/docs/1.x/api/fast-animation.scrolltrigger.md`)
id: fast-animation.scrolltrigger title: ScrollTrigger class hide_title: true layout: 1x
@microsoft/fast-animation > ScrollTrigger
ScrollTrigger class
Utility for registering element/callback pairs where the callback will be called on scroll while the element is in view.
Signature:
export default class ScrollTrigger extends ScrollBaseMethods
| Method | Modifiers | Description |
|---|---|---|
| update() | Check if elements are in view-port and apply scroll method if they are |
Sites Website Src Docs 1.X Api Fast Animation.Scrolltrigger.Update (`sites/website/src/docs/1.x/api/fast-animation.scrolltrigger.update.md`)
id: fast-animation.scrolltrigger.update title: ScrollTrigger.update() method hide_title: true layout: 1x
@microsoft/fast-animation > ScrollTrigger > update
ScrollTrigger.update() method
Check if elements are in view-port and apply scroll method if they are
Signature:
protected update(): void;Returns:
void
Sites Website Src Docs 1.X Api Fast Animation.Viewentertrigger (`sites/website/src/docs/1.x/api/fast-animation.viewentertrigger.md`)
id: fast-animation.viewentertrigger title: ViewEnterTrigger class hide_title: true layout: 1x
@microsoft/fast-animation > ViewEnterTrigger
ViewEnterTrigger class
Utility for registering element/callback pairs where the callback will be called when the element enters the view-port
Signature:
export default class ViewEnterTrigger extends ScrollBaseMethods
| Method | Modifiers | Description |
|---|---|---|
| update() | Check if elements are in view-port and apply scroll method if they are |
Sites Website Src Docs 1.X Api Fast Animation.Viewentertrigger.Update (`sites/website/src/docs/1.x/api/fast-animation.viewentertrigger.update.md`)
id: fast-animation.viewentertrigger.update title: ViewEnterTrigger.update() method hide_title: true layout: 1x
@microsoft/fast-animation > ViewEnterTrigger > update
ViewEnterTrigger.update() method
Check if elements are in view-port and apply scroll method if they are
Signature:
protected update(): void;Returns:
void
Sites Website Src Docs 1.X Api Fast Animation.Viewexittrigger (`sites/website/src/docs/1.x/api/fast-animation.viewexittrigger.md`)
id: fast-animation.viewexittrigger title: ViewExitTrigger class hide_title: true layout: 1x
@microsoft/fast-animation > ViewExitTrigger
ViewExitTrigger class
Utility for registering element/callback pairs where the callback will be invoked when the element exits the view-port
Signature:
export default class ViewExitTrigger extends ScrollBaseMethods
| Method | Modifiers | Description |
|---|---|---|
| update() | Check if elements are in view-port and apply scroll method if they are |
Sites Website Src Docs 1.X Api Fast Animation.Viewexittrigger.Update (`sites/website/src/docs/1.x/api/fast-animation.viewexittrigger.update.md`)
id: fast-animation.viewexittrigger.update title: ViewExitTrigger.update() method hide_title: true layout: 1x
@microsoft/fast-animation > ViewExitTrigger > update
ViewExitTrigger.update() method
Check if elements are in view-port and apply scroll method if they are
Signature:
protected update(): void;Returns:
void
Sites Website Src Docs 1.X Api Fast Colors.Blend (`sites/website/src/docs/1.x/api/fast-colors.blend.md`)
id: fast-colors.blend title: blend() function hide_title: true layout: 1x
@microsoft/fast-colors > blend
blend() function
Blend two colors.
Signature:
export declare function blend(mode: ColorBlendMode, bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| mode | ColorBlendMode | |
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blendburn (`sites/website/src/docs/1.x/api/fast-colors.blendburn.md`)
id: fast-colors.blendburn title: blendBurn() function hide_title: true layout: 1x
@microsoft/fast-colors > blendBurn
blendBurn() function
Blends two colors with the burn mode
Signature:
export declare function blendBurn(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blendburnchannel (`sites/website/src/docs/1.x/api/fast-colors.blendburnchannel.md`)
id: fast-colors.blendburnchannel title: blendBurnChannel() function hide_title: true layout: 1x
@microsoft/fast-colors > blendBurnChannel
blendBurnChannel() function
Signature:
export declare function blendBurnChannel(bottom: number, top: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | number | |
| top | number |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Blendcolor (`sites/website/src/docs/1.x/api/fast-colors.blendcolor.md`)
id: fast-colors.blendcolor title: blendColor() function hide_title: true layout: 1x
@microsoft/fast-colors > blendColor
blendColor() function
Blends two colors
Signature:
export declare function blendColor(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blenddarken (`sites/website/src/docs/1.x/api/fast-colors.blenddarken.md`)
id: fast-colors.blenddarken title: blendDarken() function hide_title: true layout: 1x
@microsoft/fast-colors > blendDarken
blendDarken() function
Blends two colors with the darken mode
Signature:
export declare function blendDarken(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blenddarkenchannel (`sites/website/src/docs/1.x/api/fast-colors.blenddarkenchannel.md`)
id: fast-colors.blenddarkenchannel title: blendDarkenChannel() function hide_title: true layout: 1x
@microsoft/fast-colors > blendDarkenChannel
blendDarkenChannel() function
Signature:
export declare function blendDarkenChannel(bottom: number, top: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | number | |
| top | number |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Blenddodge (`sites/website/src/docs/1.x/api/fast-colors.blenddodge.md`)
id: fast-colors.blenddodge title: blendDodge() function hide_title: true layout: 1x
@microsoft/fast-colors > blendDodge
blendDodge() function
Blends two colors with the dodge mode
Signature:
export declare function blendDodge(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blenddodgechannel (`sites/website/src/docs/1.x/api/fast-colors.blenddodgechannel.md`)
id: fast-colors.blenddodgechannel title: blendDodgeChannel() function hide_title: true layout: 1x
@microsoft/fast-colors > blendDodgeChannel
blendDodgeChannel() function
Signature:
export declare function blendDodgeChannel(bottom: number, top: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | number | |
| top | number |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Blendlighten (`sites/website/src/docs/1.x/api/fast-colors.blendlighten.md`)
id: fast-colors.blendlighten title: blendLighten() function hide_title: true layout: 1x
@microsoft/fast-colors > blendLighten
blendLighten() function
Blends two colors with the lighten mode
Signature:
export declare function blendLighten(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blendlightenchannel (`sites/website/src/docs/1.x/api/fast-colors.blendlightenchannel.md`)
id: fast-colors.blendlightenchannel title: blendLightenChannel() function hide_title: true layout: 1x
@microsoft/fast-colors > blendLightenChannel
blendLightenChannel() function
Signature:
export declare function blendLightenChannel(bottom: number, top: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | number | |
| top | number |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Blendmultiply (`sites/website/src/docs/1.x/api/fast-colors.blendmultiply.md`)
id: fast-colors.blendmultiply title: blendMultiply() function hide_title: true layout: 1x
@microsoft/fast-colors > blendMultiply
blendMultiply() function
Blends two colors with the multiply mode
Signature:
export declare function blendMultiply(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blendmultiplychannel (`sites/website/src/docs/1.x/api/fast-colors.blendmultiplychannel.md`)
id: fast-colors.blendmultiplychannel title: blendMultiplyChannel() function hide_title: true layout: 1x
@microsoft/fast-colors > blendMultiplyChannel
blendMultiplyChannel() function
Signature:
export declare function blendMultiplyChannel(bottom: number, top: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | number | |
| top | number |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Blendoverlay (`sites/website/src/docs/1.x/api/fast-colors.blendoverlay.md`)
id: fast-colors.blendoverlay title: blendOverlay() function hide_title: true layout: 1x
@microsoft/fast-colors > blendOverlay
blendOverlay() function
Blends two colors with the overlay mode
Signature:
export declare function blendOverlay(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blendoverlaychannel (`sites/website/src/docs/1.x/api/fast-colors.blendoverlaychannel.md`)
id: fast-colors.blendoverlaychannel title: blendOverlayChannel() function hide_title: true layout: 1x
@microsoft/fast-colors > blendOverlayChannel
blendOverlayChannel() function
Signature:
export declare function blendOverlayChannel(bottom: number, top: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | number | |
| top | number |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Blendscreen (`sites/website/src/docs/1.x/api/fast-colors.blendscreen.md`)
id: fast-colors.blendscreen title: blendScreen() function hide_title: true layout: 1x
@microsoft/fast-colors > blendScreen
blendScreen() function
Blends two colors with the screen mode
Signature:
export declare function blendScreen(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | ColorRGBA64 | |
| top | ColorRGBA64 |
Returns:
Remarks
The alpha channel of the input is ignored
Sites Website Src Docs 1.X Api Fast Colors.Blendscreenchannel (`sites/website/src/docs/1.x/api/fast-colors.blendscreenchannel.md`)
id: fast-colors.blendscreenchannel title: blendScreenChannel() function hide_title: true layout: 1x
@microsoft/fast-colors > blendScreenChannel
blendScreenChannel() function
Signature:
export declare function blendScreenChannel(bottom: number, top: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| bottom | number | |
| top | number |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Calculateoverlaycolor (`sites/website/src/docs/1.x/api/fast-colors.calculateoverlaycolor.md`)
id: fast-colors.calculateoverlaycolor title: calculateOverlayColor() function hide_title: true layout: 1x
@microsoft/fast-colors > calculateOverlayColor
calculateOverlayColor() function
Calculate an overlay color that uses rgba (rgb + alpha) that matches the appearance of a given solid color when placed on the same background
Signature:
export declare function calculateOverlayColor(rgbMatch: ColorRGBA64, rgbBackground: ColorRGBA64, rgbOverlay?: ColorRGBA64): ColorRGBA64;Parameters
| Parameter | Type | Description |
|---|---|---|
| rgbMatch | ColorRGBA64 | The solid color the overlay should match in appearance when placed over the rgbBackground |
| rgbBackground | ColorRGBA64 | The background on which the overlay rests |
| rgbOverlay | ColorRGBA64 | The rgb color of the overlay. Typically this is either pure white or pure black and when not provided will be determined automatically. This color will be used in the returned output |
Returns:
The rgba (rgb + alpha) color of the overlay
Sites Website Src Docs 1.X Api Fast Colors.Centeredrescale (`sites/website/src/docs/1.x/api/fast-colors.centeredrescale.md`)
id: fast-colors.centeredrescale title: centeredRescale() function hide_title: true layout: 1x
@microsoft/fast-colors > centeredRescale
centeredRescale() function
Takes an input array of colors and extrapolates them to a larger palette. The mapping first takes the input array and extrapolates between each color so that they are separated by spacing-1 slots. Then it adds to either end enough new colors to make up the desired targetSize. All output color slots between the defined stops are interpolated.
Signature:
export declare function centeredRescale(input: ColorRGBA64[], config?: CenteredRescaleConfig): ColorRGBA64[];Parameters
| Parameter | Type | Description |
|---|---|---|
| input | ColorRGBA64[] | |
| config | CenteredRescaleConfig |
Returns:
Example
For an input array with length 5, a targetSize of 17 and spacing of 3 the output would be: 0: scaleColorLight 1: 2: input 0 3: 4: 5: input 1 6: 7: 8: input 2 9: 10: 11: input 3 12: 13: 14: input 4 15: 16: scaleColorDark
Sites Website Src Docs 1.X Api Fast Colors.Centeredrescaleconfig (`sites/website/src/docs/1.x/api/fast-colors.centeredrescaleconfig.md`)
id: fast-colors.centeredrescaleconfig title: CenteredRescaleConfig interface hide_title: true layout: 1x
@microsoft/fast-colors > CenteredRescaleConfig
CenteredRescaleConfig interface
Signature:
export interface CenteredRescaleConfigProperties
| Property | Type | Description |
|---|---|---|
| scaleColorDark | ColorRGBA64 | |
| scaleColorLight | ColorRGBA64 | |
| spacing | number | |
| targetSize | number |
Sites Website Src Docs 1.X Api Fast Colors.Centeredrescaleconfig.Scalecolordark (`sites/website/src/docs/1.x/api/fast-colors.centeredrescaleconfig.scalecolordark.md`)
id: fast-colors.centeredrescaleconfig.scalecolordark title: CenteredRescaleConfig.scaleColorDark property hide_title: true layout: 1x
@microsoft/fast-colors > CenteredRescaleConfig > scaleColorDark
CenteredRescaleConfig.scaleColorDark property
Signature:
scaleColorDark: ColorRGBA64;Sites Website Src Docs 1.X Api Fast Colors.Centeredrescaleconfig.Scalecolorlight (`sites/website/src/docs/1.x/api/fast-colors.centeredrescaleconfig.scalecolorlight.md`)
id: fast-colors.centeredrescaleconfig.scalecolorlight title: CenteredRescaleConfig.scaleColorLight property hide_title: true layout: 1x
@microsoft/fast-colors > CenteredRescaleConfig > scaleColorLight
CenteredRescaleConfig.scaleColorLight property
Signature:
scaleColorLight: ColorRGBA64;Sites Website Src Docs 1.X Api Fast Colors.Centeredrescaleconfig.Spacing (`sites/website/src/docs/1.x/api/fast-colors.centeredrescaleconfig.spacing.md`)
id: fast-colors.centeredrescaleconfig.spacing title: CenteredRescaleConfig.spacing property hide_title: true layout: 1x
@microsoft/fast-colors > CenteredRescaleConfig > spacing
CenteredRescaleConfig.spacing property
Signature:
spacing: number;Sites Website Src Docs 1.X Api Fast Colors.Centeredrescaleconfig.Targetsize (`sites/website/src/docs/1.x/api/fast-colors.centeredrescaleconfig.targetsize.md`)
id: fast-colors.centeredrescaleconfig.targetsize title: CenteredRescaleConfig.targetSize property hide_title: true layout: 1x
@microsoft/fast-colors > CenteredRescaleConfig > targetSize
CenteredRescaleConfig.targetSize property
Signature:
targetSize: number;Sites Website Src Docs 1.X Api Fast Colors.Clamp (`sites/website/src/docs/1.x/api/fast-colors.clamp.md`)
id: fast-colors.clamp title: clamp() function hide_title: true layout: 1x
@microsoft/fast-colors > clamp
clamp() function
Ensures that an input number does not exceed a max value and is not less than a min value.
Signature:
export declare function clamp(i: number, min: number, max: number): number;Parameters
| Parameter | Type | Description |
|---|---|---|
| i | number | the number to clamp |
| min | number | the maximum (inclusive) value |
| max | number | the minimum (inclusive) value |
Returns:
number
Sites Website Src Docs 1.X Api Fast Colors.Colorblendmode (`sites/website/src/docs/1.x/api/fast-colors.colorblendmode.md`)
id: fast-colors.colorblendmode title: ColorBlendMode enum hide_title: true layout: 1x
@microsoft/fast-colors > ColorBlendMode
ColorBlendMode enum
Color blend modes.
Signature:
export declare enum ColorBlendModeEnumeration Members
| Member | Value | Description |
|---|---|---|
| Burn | 0 |
|
| Color | 1 |
|
| Darken | 2 |
|
| Dodge | 3 |
|
| Lighten | 4 |
|
| Multiply | 5 |
|
| Overlay | 6 |
|
| Screen | 7 |
Sites Website Src Docs 1.X Api Fast Colors.Colorhsl. Constructor (`sites/website/src/docs/1.x/api/fast-colors.colorhsl._constructor_.md`)
id: fast-colors.colorhsl._constructor_ title: ColorHSL.(constructor) hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > (constructor)
ColorHSL.(constructor)
Constructs a new instance of the ColorHSL class
Signature:
constructor(hue: number, sat: number, lum: number);Parameters
| Parameter | Type | Description |
|---|---|---|
| hue | number | |
| sat | number | |
| lum | number |
Sites Website Src Docs 1.X Api Fast Colors.Colorhsl.Equalvalue (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.equalvalue.md`)
id: fast-colors.colorhsl.equalvalue title: ColorHSL.equalValue() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > equalValue
ColorHSL.equalValue() method
Determines if a color is equal to another
Signature:
equalValue(rhs: ColorHSL): boolean;Parameters
| Parameter | Type | Description |
|---|---|---|
| rhs | ColorHSL | the value to compare |
Returns:
boolean
Sites Website Src Docs 1.X Api Fast Colors.Colorhsl.Fromobject (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.fromobject.md`)
id: fast-colors.colorhsl.fromobject title: ColorHSL.fromObject() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > fromObject
ColorHSL.fromObject() method
Construct a ColorHSL from a config object.
Signature:
static fromObject(data: {
h: number;
s: number;
l: number;
}): ColorHSL | null;Parameters
| Parameter | Type | Description |
|---|---|---|
| data | { h: number; s: number; l: number; } |
Returns:
ColorHSL | null
Sites Website Src Docs 1.X Api Fast Colors.Colorhsl.H (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.h.md`)
id: fast-colors.colorhsl.h title: ColorHSL.h property hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > h
ColorHSL.h property
Signature:
readonly h: number;Sites Website Src Docs 1.X Api Fast Colors.Colorhsl.L (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.l.md`)
id: fast-colors.colorhsl.l title: ColorHSL.l property hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > l
ColorHSL.l property
Signature:
readonly l: number;Sites Website Src Docs 1.X Api Fast Colors.Colorhsl (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.md`)
id: fast-colors.colorhsl title: ColorHSL class hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL
ColorHSL class
This uses Hue values in "degree" format. So expect a range of [0,360]. Some other implementations instead uses radians or a normalized Hue with range [0,1]. Be aware of this when checking values or using other libraries.
Signature:
export declare class ColorHSLConstructors
| Constructor | Modifiers | Description |
|---|---|---|
| (constructor)(hue, sat, lum) | Constructs a new instance of the ColorHSL class |
Properties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| h | number | ||
| l | number | ||
| s | number |
Methods
| Method | Modifiers | Description |
|---|---|---|
| equalValue(rhs) | Determines if a color is equal to another | |
| fromObject(data) | static |
Construct a ColorHSL from a config object. |
| roundToPrecision(precision) | Returns a new ColorHSL rounded to the provided precision | |
| toObject() | Returns the ColorHSL formatted as an object. |
Sites Website Src Docs 1.X Api Fast Colors.Colorhsl.Roundtoprecision (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.roundtoprecision.md`)
id: fast-colors.colorhsl.roundtoprecision title: ColorHSL.roundToPrecision() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > roundToPrecision
ColorHSL.roundToPrecision() method
Returns a new ColorHSL rounded to the provided precision
Signature:
roundToPrecision(precision: number): ColorHSL;Parameters
| Parameter | Type | Description |
|---|---|---|
| precision | number | the precision to round to |
Returns:
Sites Website Src Docs 1.X Api Fast Colors.Colorhsl.S (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.s.md`)
id: fast-colors.colorhsl.s title: ColorHSL.s property hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > s
ColorHSL.s property
Signature:
readonly s: number;Sites Website Src Docs 1.X Api Fast Colors.Colorhsl.Toobject (`sites/website/src/docs/1.x/api/fast-colors.colorhsl.toobject.md`)
id: fast-colors.colorhsl.toobject title: ColorHSL.toObject() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSL > toObject
ColorHSL.toObject() method
Returns the ColorHSL formatted as an object.
Signature:
toObject(): {
h: number;
s: number;
l: number;
};Returns:
{ h: number; s: number; l: number; }
Sites Website Src Docs 1.X Api Fast Colors.Colorhsv. Constructor (`sites/website/src/docs/1.x/api/fast-colors.colorhsv._constructor_.md`)
id: fast-colors.colorhsv._constructor_ title: ColorHSV.(constructor) hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > (constructor)
ColorHSV.(constructor)
Constructs a new instance of the ColorHSV class
Signature:
constructor(hue: number, sat: number, val: number);Parameters
| Parameter | Type | Description |
|---|---|---|
| hue | number | |
| sat | number | |
| val | number |
Sites Website Src Docs 1.X Api Fast Colors.Colorhsv.Equalvalue (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.equalvalue.md`)
id: fast-colors.colorhsv.equalvalue title: ColorHSV.equalValue() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > equalValue
ColorHSV.equalValue() method
Determines if a color is equal to another
Signature:
equalValue(rhs: ColorHSV): boolean;Parameters
| Parameter | Type | Description |
|---|---|---|
| rhs | ColorHSV | the value to compare |
Returns:
boolean
Sites Website Src Docs 1.X Api Fast Colors.Colorhsv.Fromobject (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.fromobject.md`)
id: fast-colors.colorhsv.fromobject title: ColorHSV.fromObject() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > fromObject
ColorHSV.fromObject() method
Construct a ColorHSV from a config object.
Signature:
static fromObject(data: {
h: number;
s: number;
v: number;
}): ColorHSV | null;Parameters
| Parameter | Type | Description |
|---|---|---|
| data | { h: number; s: number; v: number; } |
Returns:
ColorHSV | null
Sites Website Src Docs 1.X Api Fast Colors.Colorhsv.H (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.h.md`)
id: fast-colors.colorhsv.h title: ColorHSV.h property hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > h
ColorHSV.h property
Signature:
readonly h: number;Sites Website Src Docs 1.X Api Fast Colors.Colorhsv (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.md`)
id: fast-colors.colorhsv title: ColorHSV class hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV
ColorHSV class
This uses Hue values in "degree" format. So expect a range of [0,360]. Some other implementations instead uses radians or a normalized Hue with range [0,1]. Be aware of this when checking values or using other libraries.
Signature:
export declare class ColorHSVConstructors
| Constructor | Modifiers | Description |
|---|---|---|
| (constructor)(hue, sat, val) | Constructs a new instance of the ColorHSV class |
Properties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| h | number | ||
| s | number | ||
| v | number |
Methods
| Method | Modifiers | Description |
|---|---|---|
| equalValue(rhs) | Determines if a color is equal to another | |
| fromObject(data) | static |
Construct a ColorHSV from a config object. |
| roundToPrecision(precision) | Returns a new ColorHSV rounded to the provided precision | |
| toObject() | Returns the ColorHSV formatted as an object. |
Sites Website Src Docs 1.X Api Fast Colors.Colorhsv.Roundtoprecision (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.roundtoprecision.md`)
id: fast-colors.colorhsv.roundtoprecision title: ColorHSV.roundToPrecision() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > roundToPrecision
ColorHSV.roundToPrecision() method
Returns a new ColorHSV rounded to the provided precision
Signature:
roundToPrecision(precision: number): ColorHSV;Parameters
| Parameter | Type | Description |
|---|---|---|
| precision | number | the precision to round to |
Returns:
Sites Website Src Docs 1.X Api Fast Colors.Colorhsv.S (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.s.md`)
id: fast-colors.colorhsv.s title: ColorHSV.s property hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > s
ColorHSV.s property
Signature:
readonly s: number;Sites Website Src Docs 1.X Api Fast Colors.Colorhsv.Toobject (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.toobject.md`)
id: fast-colors.colorhsv.toobject title: ColorHSV.toObject() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > toObject
ColorHSV.toObject() method
Returns the ColorHSV formatted as an object.
Signature:
toObject(): {
h: number;
s: number;
v: number;
};Returns:
{ h: number; s: number; v: number; }
Sites Website Src Docs 1.X Api Fast Colors.Colorhsv.V (`sites/website/src/docs/1.x/api/fast-colors.colorhsv.v.md`)
id: fast-colors.colorhsv.v title: ColorHSV.v property hide_title: true layout: 1x
@microsoft/fast-colors > ColorHSV > v
ColorHSV.v property
Signature:
readonly v: number;Sites Website Src Docs 1.X Api Fast Colors.Colorinterpolationspace (`sites/website/src/docs/1.x/api/fast-colors.colorinterpolationspace.md`)
id: fast-colors.colorinterpolationspace title: ColorInterpolationSpace enum hide_title: true layout: 1x
@microsoft/fast-colors > ColorInterpolationSpace
ColorInterpolationSpace enum
Color interpolation spaces
Signature:
export declare enum ColorInterpolationSpaceEnumeration Members
| Member | Value | Description |
|---|---|---|
| HSL | 1 |
|
| HSV | 2 |
|
| LAB | 4 |
|
| LCH | 5 |
|
| RGB | 0 |
|
| XYZ | 3 |
Sites Website Src Docs 1.X Api Fast Colors.Colorlab. Constructor (`sites/website/src/docs/1.x/api/fast-colors.colorlab._constructor_.md`)
id: fast-colors.colorlab._constructor_ title: ColorLAB.(constructor) hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > (constructor)
ColorLAB.(constructor)
Constructs a new instance of the ColorLAB class
Signature:
constructor(l: number, a: number, b: number);Parameters
| Parameter | Type | Description |
|---|---|---|
| l | number | |
| a | number | |
| b | number |
Sites Website Src Docs 1.X Api Fast Colors.Colorlab.A (`sites/website/src/docs/1.x/api/fast-colors.colorlab.a.md`)
id: fast-colors.colorlab.a title: ColorLAB.a property hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > a
ColorLAB.a property
Signature:
readonly a: number;Sites Website Src Docs 1.X Api Fast Colors.Colorlab.B (`sites/website/src/docs/1.x/api/fast-colors.colorlab.b.md`)
id: fast-colors.colorlab.b title: ColorLAB.b property hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > b
ColorLAB.b property
Signature:
readonly b: number;Sites Website Src Docs 1.X Api Fast Colors.Colorlab.Epsilon (`sites/website/src/docs/1.x/api/fast-colors.colorlab.epsilon.md`)
id: fast-colors.colorlab.epsilon title: ColorLAB.epsilon property hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > epsilon
ColorLAB.epsilon property
Signature:
static readonly epsilon: number;Sites Website Src Docs 1.X Api Fast Colors.Colorlab.Equalvalue (`sites/website/src/docs/1.x/api/fast-colors.colorlab.equalvalue.md`)
id: fast-colors.colorlab.equalvalue title: ColorLAB.equalValue() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > equalValue
ColorLAB.equalValue() method
Determines if a color is equal to another
Signature:
equalValue(rhs: ColorLAB): boolean;Parameters
| Parameter | Type | Description |
|---|---|---|
| rhs | ColorLAB | the value to compare |
Returns:
boolean
Sites Website Src Docs 1.X Api Fast Colors.Colorlab.Fromobject (`sites/website/src/docs/1.x/api/fast-colors.colorlab.fromobject.md`)
id: fast-colors.colorlab.fromobject title: ColorLAB.fromObject() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > fromObject
ColorLAB.fromObject() method
Construct a ColorLAB from a config object.
Signature:
static fromObject(data: {
l: number;
a: number;
b: number;
}): ColorLAB | null;Parameters
| Parameter | Type | Description |
|---|---|---|
| data | { l: number; a: number; b: number; } |
Returns:
ColorLAB | null
Sites Website Src Docs 1.X Api Fast Colors.Colorlab.Kappa (`sites/website/src/docs/1.x/api/fast-colors.colorlab.kappa.md`)
id: fast-colors.colorlab.kappa title: ColorLAB.kappa property hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > kappa
ColorLAB.kappa property
Signature:
static readonly kappa: number;Sites Website Src Docs 1.X Api Fast Colors.Colorlab.L (`sites/website/src/docs/1.x/api/fast-colors.colorlab.l.md`)
id: fast-colors.colorlab.l title: ColorLAB.l property hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > l
ColorLAB.l property
Signature:
readonly l: number;Sites Website Src Docs 1.X Api Fast Colors.Colorlab (`sites/website/src/docs/1.x/api/fast-colors.colorlab.md`)
id: fast-colors.colorlab title: ColorLAB class hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB
ColorLAB class
CIELAB color space This implementation uses the D65 constants for 2 degrees. That determines the constants used for the pure white point of the XYZ space of 0.95047, 1.0, 1.08883. https://en.wikipedia.org/wiki/Illuminant_D65 These constants determine how the XYZ, LCH and LAB colors convert to/from RGB.
Signature:
export declare class ColorLABConstructors
| Constructor | Modifiers | Description |
|---|---|---|
| (constructor)(l, a, b) | Constructs a new instance of the ColorLAB class |
Properties
| Property | Modifiers | Type | Description |
|---|---|---|---|
| a | number | ||
| b | number | ||
| epsilon | static |
number | |
| kappa | static |
number | |
| l | number |
Methods
| Method | Modifiers | Description |
|---|---|---|
| equalValue(rhs) | Determines if a color is equal to another | |
| fromObject(data) | static |
Construct a ColorLAB from a config object. |
| roundToPrecision(precision) | Returns a new ColorLAB rounded to the provided precision | |
| toObject() | Returns the ColorLAB formatted as an object. |
Sites Website Src Docs 1.X Api Fast Colors.Colorlab.Roundtoprecision (`sites/website/src/docs/1.x/api/fast-colors.colorlab.roundtoprecision.md`)
id: fast-colors.colorlab.roundtoprecision title: ColorLAB.roundToPrecision() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > roundToPrecision
ColorLAB.roundToPrecision() method
Returns a new ColorLAB rounded to the provided precision
Signature:
roundToPrecision(precision: number): ColorLAB;Parameters
| Parameter | Type | Description |
|---|---|---|
| precision | number | the precision to round to |
Returns:
Sites Website Src Docs 1.X Api Fast Colors.Colorlab.Toobject (`sites/website/src/docs/1.x/api/fast-colors.colorlab.toobject.md`)
id: fast-colors.colorlab.toobject title: ColorLAB.toObject() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorLAB > toObject
ColorLAB.toObject() method
Returns the ColorLAB formatted as an object.
Signature:
toObject(): {
l: number;
a: number;
b: number;
};Returns:
{ l: number; a: number; b: number; }
Sites Website Src Docs 1.X Api Fast Colors.Colorlch. Constructor (`sites/website/src/docs/1.x/api/fast-colors.colorlch._constructor_.md`)
id: fast-colors.colorlch._constructor_ title: ColorLCH.(constructor) hide_title: true layout: 1x
@microsoft/fast-colors > ColorLCH > (constructor)
ColorLCH.(constructor)
Constructs a new instance of the ColorLCH class
Signature:
constructor(l: number, c: number, h: number);Parameters
| Parameter | Type | Description |
|---|---|---|
| l | number | |
| c | number | |
| h | number |
Sites Website Src Docs 1.X Api Fast Colors.Colorlch.C (`sites/website/src/docs/1.x/api/fast-colors.colorlch.c.md`)
id: fast-colors.colorlch.c title: ColorLCH.c property hide_title: true layout: 1x
@microsoft/fast-colors > ColorLCH > c
ColorLCH.c property
Signature:
readonly c: number;Sites Website Src Docs 1.X Api Fast Colors.Colorlch.Equalvalue (`sites/website/src/docs/1.x/api/fast-colors.colorlch.equalvalue.md`)
id: fast-colors.colorlch.equalvalue title: ColorLCH.equalValue() method hide_title: true layout: 1x
@microsoft/fast-colors > ColorLCH > equalValue
ColorLCH.equalValue() method
Determines if one color is equal to another.
Signature:
equalValue(rhs: ColorLCH): boolean;Parameters
| Parameter | Type | Description |
|---|---|---|
| rhs | ColorLCH | the color to compare |
Returns:
boolean