fast Documentation

The adaptive interface system for modern web experiences.

RAW Doc

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.

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 the
data-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:

Creating a new example app

  1. Scaffold a new folder under the appropriate rendering-strategy subfolder
    (CSR apps go under examples/csr/<your-app>/; SSR apps go under
    examples/ssr/<your-app>/).
  2. Use examples/csr/todo-app/ as a reference for CSR
    apps, or examples/ssr/chat-app/ /
    examples/ssr/webui-todo-app/ as a reference
    for SSR apps (package.json, tsconfig.json, build config, entry HTML).
  3. Add "@microsoft/fast-examples-design-system": "^1.0.0" to the new
    app's dependencies and run npm install from the repo root.
  4. Import @microsoft/fast-examples-design-system/tokens.css exactly once at
    the app entry point (typically src/main.ts or src/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.
  5. Reference tokens via var(--fast-...) in component css template
    literals or .css files — do not hard-code design values.
  6. If the app needs a theme toggle, set or remove the data-theme attribute
    on <html> from your app code:
    ts
    document.documentElement.setAttribute("data-theme", "dark");
    document.documentElement.removeAttribute("data-theme"); // restore system
    Do not import any JavaScript from the design-system package — it
    intentionally exposes none.
  7. Add the new app to the table at the top of this file.

Running an example

From the repository root:

shell
npm install
npm start -w @microsoft/fast-<your-app>-example

For the existing example apps:

shell
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-example

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): The pull_request trigger includes ready_for_review in 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): The pr trigger uses drafts: false to 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 on workflow_dispatch. It does not bump versions or push source changes to main — version bumps land on main through ordinary human-authored pull requests (for example, by running npm run bump locally 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:
    1. detect — checks out main with fetch-depth: 0 and runs build/scripts/create-github-releases.mjs --check-only. The script walks the workspaces tree (no npm ci required), computes ${name}_v${version} for every non-private workspace, and emits hasMissingReleases=true if any of those git tags do not yet exist.
    2. release runs only when missing releases exist. Installs Node, the Rust toolchain (for cargo 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 into publish_artifacts_npm/, packs any paired Rust crates into publish_artifacts_crates/, and creates the GitHub release with all assets attached via gh release create --target <sha>. @microsoft/fast-build is a bundled release: it uses one npm package, one tag, and one GitHub release containing both microsoft-fast-build and microsoft-fast-convert crate assets. The gh CLI 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: the postbump hook in beachball.config.js rewrites each crate's Cargo.toml (and the matching entry in Cargo.lock) automatically whenever npm run bump bumps 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) with always: true so 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:
    1. Check — runs build/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 a deployed/<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.
    2. Package — depends on Check and runs only when needsDeployment == 'true'. Its PrepareReleaseInputs job downloads undeployed release assets through the fast GitHub service connection, sorts them into publish_artifacts_npm/ (.tgz) and publish_artifacts_crates/ (.crate), configures the detected npm dist-tag, and publishes those files as a governed pipeline artifact. The checkout-free Deploy release job consumes that artifact through templateContext.inputs, then FAST.Release.PipelineTemplate.yml@fastPipelines performs the actual npm publish / cargo publish. After publishing succeeds, the ordinary MarkDeployed job checks out the repository and pushes a deployed/<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:

  1. Ensure the workspace is included in the root package.json workspaces list and has a name and version.
  2. 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-build is the special bundled release and pairs with both crates/microsoft-fast-build/Cargo.toml and crates/microsoft-fast-convert/Cargo.toml.
  3. Add package-specific output variables to the Package stage in azure-pipelines-cd.yml. The output prefix is generated from the npm package name by converting @microsoft/<name> to camel case. For example, @microsoft/fast-foo emits fastFooNeedsDeployment and fastFooReleaseTag.
  4. Add a conditional DownloadGitHubRelease@0 task to PrepareReleaseInputs using the fast GitHub 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 governed fast-release-inputs pipeline artifact.
  5. Confirm the artifact sorting step still covers the package assets. Packages should attach .tgz assets, and paired crates should also attach .crate assets.

Example Azure additions for @microsoft/fast-foo:

yml
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:

toml
[dependencies]
microsoft-fast-build = "0.1"

Or use cargo add:

sh
cargo add microsoft-fast-build

Usage

Render with a JSON string

rust
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`

rust
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.

rust
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 and
render_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.

rust
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 end
SourcePosition records. Positions refer to the complete original input to
parse_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) and
render_shadow_root(elementName, templatesJson, state?, attributeNameStrategy?, markers_v2?). Generated declarations include FTemplateMetadata,
TemplateBinding, TemplateBindingScope, TemplateSourceSpan, and
TemplateSourcePosition. Each generated record has
formatVersion: 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
html
<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.

html
<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
html
<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:

html
<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:

html
<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:

html
<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:

html
<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:

html
<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 — ``

html
<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:

html
<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 — ``

html
<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.

html
<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:

html
<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:

html
{{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:

html
<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

rust
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_patterns walks 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:

html
<f-template name="my-button">
    <template>
        <button>{{label}}</button>
    </template>
</f-template>

A single file may contain multiple templates:

html
<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:

rust
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_html must 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).
  • css must not contain a case-insensitive </style raw-text terminator sequence (e.g. </style>, </STYLE >, </style/) — this would prematurely close the inserted <style> element. Content like content: "</stylesheet>" is not affected, since style must 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

rust
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.

rust
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:

html
<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)
html
<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"}; use count="{{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:

html
<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>; shadowrootmode and shadowroot default to open when 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 use dataset.* grouping)
  • aria-* attributes (always use ARIA reflection lookup)
  • HTML global attributes with known property names (e.g. tabindex → tabIndex)
  • :prop property bindings (always lowercased)

Rust API

rust
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

javascript
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. Set
RenderConfig::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 with
enableHydration({ markers: markers_v2 }).

Content binding markers

Each {{expr}} or {{{expr}}} text binding is wrapped in data-free HTML comment markers:

html
Hello world

Markers 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:

html
<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

html
[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>, and
template_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:

text
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.html

Numeric 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:

sh
cargo bench --manifest-path crates/microsoft-fast-build/Cargo.toml --bench render
cargo test --manifest-path crates/microsoft-fast-build/Cargo.toml

Criterion 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:

sh
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=render

The 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-ref to a braced w-ref, and FAST's $e event argument to bare e. f-slotted/f-children are stripped from the output with a warning, since WebUI Framework has no equivalent; every other unsupported construct (unknown f-*, $c event context, non-braced f-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 exports template as an html tagged template.

Rust usage

rust
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 return
Result<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 by
webui-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 the
fast-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 and
ConvertOptions:

rust
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 or
Namespace.MyElement); type_source_import, when provided, also emits a matching
import 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() } for
fast-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. Existing
convert_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:

ts
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(): string

convert_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.

json
{
    "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.

ts
// src/main.ts
import "@microsoft/fast-examples-design-system/tokens.css";
html
<!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
<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.

ts
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 inherited
color-scheme. Setting data-theme="light" or data-theme="dark"
overrides color-scheme to that single value, which flips every
light-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.

ts
// 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:

bash
# 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:e2e

To run a single app's suite:

bash
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-todo

Layout

text
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 project

Adapters 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/webui project instead.

Installation

From NPM

To install the latest @microsoft/fast-build package using npm:

shell
npm install --save @microsoft/fast-build

Within your JavaScript or TypeScript code, you can then import library APIs like this:

ts
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:

shell
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
<html>
  <body>
    <h1>{{title}}</h1>
    <p>{{description}}</p>
  </body>
</html>

And a state.json:

json
{
  "title": "Hello FAST",
  "description": "Server-side rendered with WebAssembly."
}

Run:

shell
fast build --entry=index.html --state=state.json --output=output.html

Produces output.html:

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.

shell
fast build --entry=index.html --state=state.json --stream

Streaming 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:

shell
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

The 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.json from the current working directory when --state was omitted. To use a state file, pass --state=state.json or set "state": "state.json" in fast-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:

js
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 repeat
scope 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 contain
offset, line, and column, relative to the complete HTML string passed to
parse_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.

  • offset is a zero-based UTF-16 code-unit offset, compatible with JavaScript
    string slicing. It is not a UTF-8 byte offset.
  • line and column are 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: start is inclusive and end is 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 in keyword,
    matching their existing expression field. Nested scope frames are unchanged.
js
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):

ts
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:

js
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:

shell
fast build \
  --templates="./components/**/*.html" \
  --entry=index.html \
  --state=state.json \
  --output=output.html

Each 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:

html
<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 use dataset.* grouping)
  • aria-* attributes (always use ARIA reflection lookup)
  • HTML global attributes with known property names (e.g. tabindex → tabIndex)
shell
fast build \
  --templates="./components/**/*.html" \
  --attribute-name-strategy=camelCase \
  --entry=index.html \
  --state=state.json \
  --output=output.html

Configuration file

Instead of passing every option on the command line, you can place a fast-build.config.json file alongside your project files:

json
{
    "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:

shell
fast build --config=configs/my-build.json

Precedence: 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.

shell
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.js

Convert 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-named
ref/children/slotted directive (which otherwise fail to type-check once a
real TSource is supplied, because html<TSource, TParent> infers a single
TSource for the whole tagged template) can be generated already type-checked
against a concrete element type. See
microsoft-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.

json
{
    "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 except
overwrite 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:

shell
npm run build -w @microsoft/fast-build
npm run test:fixtures:convert -w @microsoft/fast-build

The fixture tests run fast convert from fixture directories that contain
fast-convert.config.json, so they verify the same default config discovery used
by fast build. Generated fixture output is written under
packages/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:

shell
npm install --save @microsoft/fast-element

Within your JavaScript or TypeScript code, you can then import library APIs like this:

ts
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.

html
<!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:

html
<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 native
globalThis, 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.

ts
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-only
FASTElement 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 and
fastElementRegistry.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 as
ElementStyles, CSSDirective, cssDirective, ComposableStyles,
HostBehavior, HostController, StyleStrategy, and StyleTarget from
@microsoft/fast-element:

ts
import { ElementStyles, css } from "@microsoft/fast-element";

When runtime state or external signals need to add or remove styles, create the
ElementStyles with css and toggle it through
this.$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() and
observerMap() 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 through
enableHydration() from @microsoft/fast-element/hydration.js.

ts
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:

ts
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, and
observerMap() 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 with
declarativeTemplate(). For non-declarative/manual schema use, import Schema
from @microsoft/fast-element and pass it on the element definition;
observerMap() can also receive
observerMap({ 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. See
docs/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:

typescript
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.

typescript
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:

typescript
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 in
fast-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 — resolves true when the element had a declarative shadow root (DSD) at connect time, regardless of whether hydration ran.
  • isHydrated: Promise — resolves true only 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> before define() 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() skips updateTarget for attribute and booleanAttribute aspect 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-in
hydrationDebugger(). Install the debugger to get a rich
"Expected … / Received …" report with the SSR HTML snippet and structured
expected / received fields on the thrown error:

typescript
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:

typescript
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.

typescript
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:

shell
npm install --save @microsoft/fast-router

Within your JavaScript or TypeScript code, you can then import library APIs like this:

ts
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

text
/* Detailed source-code truncated for AI context efficiency. */

Using the Configuration with a Router

ts
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:

shell
npm install --save-dev @microsoft/fast-test-harness

Test 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.

text
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 generation

Writing 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.

ts
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:

ts
await fastPage.setTemplate();
await fastPage.updateTemplate(fastPage.element, { attributes: { disabled: true } });

The toHaveCustomState assertion checks ElementInternals custom states:

ts
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:

html
<!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.

ts
import "./define-all.js";
import { setTheme } from "./theme.js";
setTheme(lightTheme);

SSR files

ssr.html contains comment placeholders the server fills in per request:

html
<!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.

ts
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:

ts
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):

ts
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):

ts
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:

ts
// playwright.config.ts
export default defineConfig({
    webServer: {
        command: "fast-test-harness",
        port: 3278,
        reuseExistingServer: true,
    },
});

For custom setup, import startServer:

ts
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 matching
base fixture option for each test group.

ts
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

text
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 message

CLI 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:
bash
npm install

Running benchmarks

From this folder (sites/benchmarks):

bash
npm run bench

This will:

  1. Build the scenarios with Vite (into server/dist)
  2. Start the static server on http://localhost:5174
  3. Run all discovered scenarios through Playwright
  4. Print stats tables to the console
  5. Generate an interactive HTML report at results/latest.html

Iteration count

Default is 50 iterations per benchmark. Override with BENCH_ITERATIONS:

bash
BENCH_ITERATIONS=100 npm run bench

Running specific scenarios

Pass scenario names as arguments to filter which benchmarks run:

bash
npm run bench -- basic attr-reflect

Each argument is matched as a substring, so basic runs basic/csr and basic/hydration. You can also target a specific variant:

bash
npm run bench -- basic/csr

Running from repo root

bash
npm run bench -w @microsoft/fast-bench

Project structure

text
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:

text
<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 rendering

Output

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:

bash
npm run serve

This 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:

sh
npm ci
npm run build

Local Development

sh
npm start

Build

sh
npm run build

This 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:

js
"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.

sh
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 render

The 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 and
aarch64-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 is
c749f46306b561bb55e45700cf19f7ed3db8ad67d01cd8e9a3b7198943c4ca51.
The after artifact was built from 3e9117770a8da29c369d3d31a04e37da25fa08fb;
its WASM SHA-256 is
c128e5d88ff457dedc0549438ec9e2d11450e0a89e997899b003898e587063bc.
Both reports retain their starting provenance even if the worktree changes
during measurement.

To reproduce an optimized comparison after building the package:

sh
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-scaling

Profiling 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 include
RawTable::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:

sh
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 --noplot

For 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:

text
^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:

sh
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.mjs

Single-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 through
npm 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 source
8b9c524f3a6a7544919588e708c4ad0c98a8197b; 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:

sh
node packages/fast-build/test/performance/render-entry.mjs --golden=/tmp/fast-golden.json

Review 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 run npm install and then npm start to 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 version and 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 — see
examples/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 and
examples/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-size paired with
    --fast-text-global-body3-line-height,
    --fast-text-global-display2-font-size paired 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-stroke paired with
    --fast-ctrl-focus-outer-stroke-width.
  • Motion: --fast-duration-fast and --fast-curve-easy-ease for 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

bash
# from the monorepo root
npm ci
npm run build -w @microsoft/fast-element
npm start -w @microsoft/fast-todo-mobx-app-example

The Vite dev server runs at http://localhost:9001.

To produce a production build:

bash
npm run build -w @microsoft/fast-todo-mobx-app-example

The 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.

typescript
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

  • autorun runs immediately, then re-runs whenever any MobX-observable it read changes.
  • Each re-run reassigns FAST @observable fields, 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 autorun before super.connectedCallback() ensures the mirror fields have correct values before the template binds, avoiding any initial flicker.
  • The disposer returned by autorun is 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

text
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, and
public/styles.css references the registered --fast-* tokens through
var(...).

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:

  • Hi
  • How are you?
  • What's the weather like?
  • Tell me a joke
  • What can you do?
  • Goodbye

Run locally

From the repository root:

bash
npm run build -w @microsoft/fast-chat-app-example
npm run start -w @microsoft/fast-chat-app-example

Or from this folder:

bash
npm run build
npm start

The 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 step
  • state.json — initial state used while pre-rendering the page shell
  • templates.html — declarative FAST templates for the custom elements
  • build-markup.mjs — runs @microsoft/fast-build and injects the templates into index.html
  • public/styles.css — token-based shared stylesheet for the page shell and component shadow roots
  • src/ — runtime classes, canned conversation data, and hydration bootstrap
  • index.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 .html and .css files instead of
    imperative html/css tagged template literals
  • Server-side prerendering: webui serve compiles declarative templates
    and renders initial state into static HTML
  • Client-side hydration: @microsoft/fast-element/declarative.js picks 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:

bash
npm ci
npm run build

Running

bash
# Build client bundle and start the webui dev server
npm start -w @microsoft/fast-webui-todo-app-example

The app will be available at http://localhost:8081.

For development with live reloading, run the client and server in separate
terminals:

bash
# 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-example

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/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:

  1. Scans ./src for declarative HTML templates (.html files wrapped in
    <f-template> or containing <template shadowrootmode="open">)
  2. Reads initial state from ./data/state.json
  3. Renders templates server-side with {{}} bindings resolved from state
  4. Injects Declarative Shadow DOM for instant first paint
  5. 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 and
dist/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 using FASTElement with define()

Client entry (`src/index.ts`)

Bootstraps hydration by:

  1. Enabling hydration with enableHydration({ markers: markers_v2 }) — the
    markers_v2 reader is required because the webui --plugin=fast server
    still emits FAST Element 2.x indexed hydration markers
  2. Importing components, which register themselves via subclass define() calls
    using declarativeTemplate() and observerMap()
  3. 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-element should be used by a developer and what code is executed during the first render.
  • FASTElement: How the FASTElement is architected.
  • html tagged template literal: How the html tagged template literal takes and converts the contents into a ViewTemplate.
  • Updates queue: 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 define method combines the Custom Element name, template, style, and other options into a FASTElementDefinition, then uses the native Custom Element define() to register the Custom Element with a Custom Element Registry.
  • The from method allows the use of Customized Built-in elements, which extend from native elements such as HTMLButtonElement.

Creating a Custom Element from FASTElement

A basic developer flow for defining a custom element looks like this:

mermaid
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, and
requestAnimationFrame.

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:

mermaid
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 --> I

Sites Website Src Docs 2.X Getting Started (`sites/website/src/docs/2.x/getting-started.md`)



Sites Website Src Docs 3.X Getting Started (`sites/website/src/docs/3.x/getting-started.md`)



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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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

Home

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:

shell
npm install --save @microsoft/fast-components
shell
yarn add @microsoft/fast-components

To 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:

js
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:

js
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:

html
<!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:

html
<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.

html
<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:

shell
npm install --save @microsoft/fast-element
shell
yarn add @microsoft/fast-element

Within your JavaScript or TypeScript code, you can then import library APIs like this:

ts
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.

html
<!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:

html
<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:

typescript
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:

typescript
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"

Home

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:

typescript
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:

typescript
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:

typescript
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"

Home

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:

  1. Imperative — JavaScript tagged template literals using the html function.
  2. 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):

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):

html
<f-template name="hello-world">
    <template>
        <p>{{greeting}}, world!</p>
    </template>
</f-template>

3. Use the element in your page (index.html):

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:

  1. 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.
  2. Parse — When <f-template> connects to the DOM, TemplateParser converts the declarative markup into the same internal ViewTemplate that the imperative html tag produces.
  3. Resolve — The template is resolved through the bridge and assigned to the element definition before define() completes.
  4. 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:

ts
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:

ts
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(),
});
html
<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

{% 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"

Home

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"

Home

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"

Home

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"

Home

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"

Home

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.

text
/* 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:

  1. Template Authoring – The html tagged template collects binding expressions and builds placeholder-marked HTML.
  2. Compilation – The compiler parses the placeholder HTML into a DocumentFragment, walks the DOM tree, and records where each binding targets.
  3. View Creation – The compiled result clones the fragment and creates a targets object that maps each binding to its DOM node.
  4. Binding (first render) – Each factory creates a behavior that attaches to its target node: adding event listeners, setting attributes, or observing expressions.
  5. 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.

mermaid
flowchart TD
    A["html`&lt;div class='${expr}'&gt;${text}&lt;/div&gt;`"] --> 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&lt;string, ViewBehaviorFactory&gt;"]
    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.

text
/* Detailed source-code truncated for AI context efficiency. */

3. View Creation – Cloning the Fragment & Resolving Targets

mermaid
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 --> M

4. Binding – Attaching Behaviors to DOM Nodes

mermaid
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)"]
    end

5. Reactive Update Cycle – Change Detection & DOM Updates

mermaid
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 value

6. Event Handling – Click Events and Other DOM Events

mermaid
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.

mermaid
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| O

Key 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() patches
ViewTemplate.prototype with:

  1. A Hydratable symbol — marks the template as hydration-capable (checked via isHydratable()).
  2. A hydrate(firstChild, lastChild, hostBindingTarget?) method — creates a HydrationView instead of an HTMLView.
typescript
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:

html
<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):

html
Hello, World!

Repeat Directive Markers

Each repeated item is bracketed by data-free repeat markers:

html
<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:

html
<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.

text
/* Detailed source-code truncated for AI context efficiency. */

HydrationView vs HTMLView

mermaid
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:

mermaid
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
    end

Repeat 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 in
hydration/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.

mermaid
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)"]
    end

Hydration 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-located
src/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:

sh
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=chromium

CPU timings live separately in src/hydration/hydration-benchmark.pw.spec.ts
and are opt-in rather than flaky wall-clock CI gates:

sh
FAST_HYDRATION_BENCHMARK=1 npm exec -w @microsoft/fast-element -- playwright test src/hydration/hydration-benchmark.pw.spec.ts --project=chromium --retries=0

The 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

mermaid
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 source

During 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:

typescript
// 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:

ts
import { enableHydration, hydrationDebugger } from "@microsoft/fast-element/hydration.js";

enableHydration({ debugger: hydrationDebugger() });

With the debugger installed, both errors format their message as:

text
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.

text
/* 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. Read
error.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 extending
Error; 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 and
String::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. Supply
type_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 with
render_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 expose
TemplateSourceSpan 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

mermaid
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

mermaid
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.

mermaid
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.

mermaid
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 --> F

Packages 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 browser
Window 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.

text
/* 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.

text
/* 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.

text
/* 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

  1. Overview
  2. Goals
  3. Core Concepts
  4. Package Structure
  5. Exports and Public API
  6. Template Syntax
  7. Data Flow
  8. Template Parsing Pipeline
  9. Schema and Observer Map
  10. Lifecycle
  11. Integration with fast-element
  12. Hydration Model
  13. Testing Architecture
  14. 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.

html
<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 native
HTMLElement. It is defined automatically by declarativeTemplate() in the same
CustomElementRegistry as the FAST element definition. Consumers should not
import, subclass, or define the implementation directly.

When connected to the DOM it:

  1. Registers itself with the declarative template bridge using its registry and
    name attribute.
  2. Publishes a template when a matching FAST element definition requests one.
  3. Delegates parsing of the inner <template> tag to TemplateParser, which
    converts declarative bindings into FAST ViewTemplate strings and values.
  4. Runs definition-scoped schema transforms, such as attributeMap() and
    observerMap(), before returning the concrete ViewTemplate.

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 the
strings 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 by
enableHydration().

`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 schemaMap for its own property schemas.
  • Registers itself in the module-level schemaRegistry (keyed by custom element name) for cross-element $ref resolution.

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 Proxy instances.
  • 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:

typescript
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 $observe boolean and child property overrides, allowing alternating opt-in/opt-out to arbitrary depth.

When properties is omitted, all root properties are observed. When
properties 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():

typescript
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:

  1. If properties is present and a root property is not listed, it is skipped.
  2. true/false booleans apply to the entire subtree.
  3. $observe on a node object controls the current level; children inherit when unspecified.
  4. 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}} → property fooBar, attribute foo-bar). This matches the build-time --attribute-name-strategy option in @microsoft/fast-build.
  • When attribute-name-strategy is "none", the attribute name and property name are both the binding key exactly as written in the template (e.g. {{foo-bar}} → attribute foo-bar, property foo-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 @attr or @observable are left untouched.
  • FASTElementDefinition.attributeLookup is keyed by the HTML attribute name, and propertyLookup is keyed by the JS property name so attributeChangedCallback correctly delegates to the new AttributeDefinition.

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

text
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.ts

Module 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.

text
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 before
observerMap(), so generated attributes are available to observer mapping.


Exports and Public API

typescript
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*, and
HydrationLifecycleCallbacks are not exported from the public declarative
entrypoint. Use declarativeTemplate(), attributeMap(), observerMap(), and
enableHydration() 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:

mermaid
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 --> P

Template Parsing Pipeline

The internal <f-template> publisher orchestrates the pipeline by creating a
TemplateParser 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 native HTMLElement, not a FASTElement.
  • TemplateParser (template-parser.ts) — Synchronous template parser:
    converts declarative HTML into strings/values arrays for
    ViewTemplate.create(). Uses a StringsAccumulator to 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 before ViewTemplate.create(). attributeMap() runs before
    observerMap().
text
/* 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:

typescript
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 (&gt;, &lt;, etc.) used in <f-when> expressions.
escapeBracesInCodeElements(html) Replaces { / } characters inside every <code> element with &#123; / &#125; 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 &#123; / &#125; 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

text
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 binding

Schema 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.

mermaid
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:

  • false in the config → $observe: false is stamped recursively on the node and all its descendants.
  • $observe: false on a config node → the schema node is stamped, and unlisted children inherit the stamp.
  • true in 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 properties tree, applyConfigToSchema
    is never called and no schema nodes are mutated — zero overhead for the
    common case.
  • The proxy system uses isSchemaExcluded(schema) (checks $observe === false with no observed descendants) as the single predicate for all skip/suppress decisions.
  • Schema nodes without $observe are 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 calls
defineProperties(). It iterates Schema.getRootProperties() and skips any
property whose schema entry contains properties, type, or anyOf — keeping
only plain leaf bindings. For each leaf:

  1. The schema key is used as the JS property name.
  2. 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).
  3. A new AttributeDefinition is registered via Observable.defineProperty.
  4. FASTElementDefinition.attributeLookup is keyed by the HTML attribute name and propertyLookup is keyed by the JS property name so attributeChangedCallback can 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

mermaid
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
    end

Promise 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. Set
enableHydration({ 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:

  1. The custom element tag with its attributes and initial state.
  2. A <template shadowrootmode="open"> containing pre-rendered HTML annotated with FAST's hydration markers.
  3. 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):

text

Attribute bindings use a single data-fe dataset attribute with binding count:

html
<el data-fe="3">

Repeat directives wrap each item in comment pairs:

text
...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:

text
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> definitions

Fixtures 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 generate
index.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:

  1. Build (npm run build:fixtures:webui) — runs
    scripts/declarative/build-fixtures-with-webui.js, which extracts
    <f-template> elements, converts each component template with
    fast convert --syntax=webui-prerelease, builds each fixture with
    webui build --plugin=fast, renders the protocol with state.json, and
    writes the output alongside main.ts and assets to
    temp/integrations/webui/fixtures/.
  2. 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 in
    playwright.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 the
ci-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 sets
process.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 calls
page.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 core FASTElement base class and
    element definition system.
  • @microsoft/fast-element/declarative.js: Provides the f-template
    custom element that processes HTML templates and attaches them to FAST
    elements as a ViewTemplate in lieu of an html template created during
    a subclass define() call. The preferred path uses declarativeTemplate() so
    the subclass define() 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:

html
<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 at
declarativeTemplate(). The resolver waits for a matching declarative template
and returns a concrete ViewTemplate before the platform registration step.

typescript
// 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:

  1. Template Discovery: The resolver waits for a matching
    <f-template name="..."> in the same registry as the element definition.
  2. Template Element Connection: The matching f-template element's
    connectedCallback() registers it with the declarative template bridge.
  3. Template Processing: The bridge reads and transforms the markup, builds
    the schema, applies observerMap() / attributeMap() behavior, and resolves
    data bindings, directives, and other template features into the ViewTemplate
    model which is also used by the @microsoft/fast-element html tag template.
  4. Template Attachment: The concrete ViewTemplate is returned to
    the subclass define() 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:

  1. compose() Execution: The element definition internally completes its composition process
  2. 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:

  1. Element Creation: The platform creates instances of the custom element
  2. Prerendered Content Detection: ElementController detects the existing shadow root from SSR — isPrerendered resolves true
  3. Hydration Check: If enableHydration() was called and the template is hydratable, the element hydrates — isHydrated resolves true. Otherwise it falls back to client-side rendering.
  4. Concrete Template Ready: Because declarativeTemplate() resolved during
    definition, connect() starts with the final template already attached.
  5. Hydration: ElementController uses template.hydrate() to create a
    HydrationView that maps existing DOM nodes to binding targets using fe:b
    / fe:/b markers

The DOM after hydration should look like this:

html
<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.

typescript
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 in
enableHydration() 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

  1. Initial State
  2. Hydrating Comments and Datasets

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:

html
<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):

typescript
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:

typescript
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 sets
isPrerendered = true. It then uses template.hydrate() to create a
HydrationView 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:

html
<my-component>
    <template shadowrootmode="open" shadowroot="open">
        
    </template>
</my-component>

Simple content example

Content bindings such as:

html
<h1>{{text}}</h1>

When combined with state such as:

json
{
    "text": "Hello world"
}

Should result in:

html
<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:

html
<h1 greeting="{{greeting}}"></h1>

When combined with state such as:

json
{
    "greeting": "Hello"
}

Should result in:

html
<h1 data-fe="1" greeting="Hello"></h1>

Multiple attribute bindings

When multiple attribute bindings exist on the same element:

html
<h1 greeting="{{greeting}}" subtitle="{{subtitle}}" punctuation="{{punctuation}}"></h1>

When combined with state such as:

json
{
    "greeting": "Hello",
    "subtitle": "world",
    "punctuation": "!"
}

Should result in:

html
<h1 data-fe="3" greeting="Hello" subtitle="world" punctuation="!"></h1>

Mixed attribute and content example

Multiple attributes and content bindings such as:

html
<div show="{{show}}" appearance="{{appearance}}" punctuation="{{punctuation}}">
    <h1>{{text}}</h1>
    <span>{{subtitle}}</span>
    <span>{{punctuation}}</span>
</div>

Should result in:

html
<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:

html
<f-repeat value="{{item in list}}">
    <span>{{item}}</span>
</f-repeat>

Combined with state:

json
[
    "Bob",
    "Alice",
    "Sue"
]

Should result in:

html
<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:

html

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:

html
<f-when value="{{show}}">
    <span>{{text}}</span>
</f-when>

Combined with state:

json
{
    "show": true,
    "text": "Hello world"
}

Should result in:

html
<span>
    Hello world
</span>

If the when is evaluated to falsy then we can safely leave the binding markers only.

Example state:

json
{
    "show": false,
    "text": "Hello world"
}

Should result in:

html

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:

html
<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:

json
{
    "isDisabled": true
}

Should result in:

html
<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 bare name).
  • Static name="value" is forwarded verbatim (HTML-escaped); static boolean name is emitted bare.
  • name="{{expr}}" is resolved against the element's initial child state; primitives are emitted as name="value", Array / Object / Null and missing values are stripped.
  • ?name="{{expr}}" is evaluated as a boolean against the same state; the bare name (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:

html
<button @click="{handleClick($e)}">Button</button>

Should result in:

html
<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:

html
<button f-ref="{button}">Button</button>

Should result in:

html
<button data-fe="1">Button</button>

More Examples

Nested Whens

Example when binding:

html
<f-when value="{{show}}">
    <span>{{text}}</span>
    <f-when value="{{showInternal}}">
        <span>{{internalText}}</span>
    </f-when>
</f-when>

Combined with state:

json
{
    "show": true,
    "text": "Hello world",
    "showInternal": true,
    "internalText": "Hello pluto"
}

Should result in:

html
<span>
    Hello world
</span>
    
    <span>
        Hello pluto
    </span>

Nested Repeats

Example when binding:

html
<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:

json
{
    "items": [
        {
            "name": "Bob"
        },
        {
            "name": "Alice"
        },
        {
            "name": "Sue",
            "nested": [
                {
                    "name": "Amy"
                },
                {
                    "name": "Clarice"
                },
                {
                    "name": "Lawrence"
                }
            ]
        }
    ]
}

Should result in:

html
<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":

html
<f-when value="{{showButton}}">
    <my-button appearance="{{appearance}}">{{text}}</my-button>
</f-when>

Example template of component "my-button":

html
<button class="{{appearance}}">
    <slot></slot>
</button>

Combined with state:

json
{
    "showButton": true,
    "text": "Hello world",
    "appearance": "fancy"
}

Should result in:

html
<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 and
ObserverMap 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

  1. Overview
  2. The Schema Class
  3. The Observer Map Class
  4. Integration with f-template
  5. Initial Path Processing Flow
  6. Dynamic Path Processing Updates
  7. Observable Proxy System
  8. Examples
  9. 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-element exposes 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 $c prefix (e.g., $c.parent.showNames) resolve directly from the ExecutionContext and 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

typescript
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

typescript
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 to
  • childrenMap: 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

typescript
public getSchema(rootPropertyName: string): JSONSchema | null

Retrieves 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:

typescript
{
  "$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

typescript
constructor(classPrototype: any, schema: Schema, config?: ObserverMapConfig)

Creates an observer map instance that will configure the provided class prototype using the schema information.

defineProperties

typescript
public defineProperties(): void

The main method that:

  1. Iterates through all root properties defined in the schema (each schema instance contains multiple root property schemas)
  2. Defines observable properties using FAST Element's Observable.defineProperty (an alternative to the @observable decorator syntax used in custom element classes)
  3. 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:

  1. Analyzes the schema to understand the expected object structure
  2. Creates proxies using the assignObservables utility function
  3. 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

  1. Template Parsing: When an f-template element is processed, it creates a Schema instance
  2. Binding Discovery: As template bindings are processed, paths are registered with schema.addPath()
  3. Observer Map Creation: If enabled, an ObserverMap is instantiated with the populated schema
  4. 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:

typescript
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:

typescript
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:

typescript
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:

  1. Template Binding: {{user.details.personal.name}}
  2. Path Analysis: Utilities identify root property (user) and nested path (details.personal.name)
  3. Type Check: Confirms this is a data binding (not an event handler like @click="{handleClick()}")
  4. Schema Registration:
    typescript
    schema.addPath({
      pathConfig: {
        type: "access",
        currentContext: null,
        parentContext: null,
        path: "user.details.personal.name"
      },
      rootPropertyName: "user",
      childrenMap: null
    });
  5. Schema Building: Creates nested object structure in JSON schema
  6. Observable Definition: Observer map makes user property observable
  7. Proxy Creation: When user is 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:

  1. Property Assignment: A previously undefined nested property gets assigned a value

    typescript
    // Initially: user.details was undefined
    user.details = { age: 30, location: { city: "Seattle" } };
  2. Schema Consultation: The system consults the existing schema for this root property to understand expected nested structure

  3. Proxy Creation: New proxies are automatically created for the newly assigned nested objects based on the schema definition

  4. 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:

  1. Array Mutation: Items are added via push, splice, or direct assignment

    typescript
    users.push({ name: "John", details: { age: 25 } });
  2. Array Observer Trigger: The array proxy detects the mutation and triggers observation setup

  3. Item Processing: Each new array item is processed according to the array's context schema definition

  4. 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

typescript
export function assignObservables(
    schema: JSONSchema | JSONSchemaDefinition,
    rootSchema: JSONSchema,
    data: any,
    target: any,
    rootProperty: string
): typeof Proxy

This 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:

html
<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:

typescript
// 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:

html
<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:

typescript
// 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:

html
<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:

typescript
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: objectTargetsMap and observedArraysMap use WeakMaps to automatically clean up references
  • Proxy Identification: Proxies are marked with $isProxy to 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:

typescript
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:

typescript
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:

typescript
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 with
template: 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:

html
<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:

typescript
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:

typescript
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:

typescript
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 }).

typescript
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:

typescript
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 $observe boolean and child property overrides.

Use $observe: false on a node to skip it by default, then re-include specific children:

typescript
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. When
properties 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:

typescript
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.

typescript
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.

typescript
MyElement.define(
    {
        name: "my-element",
        template: declarativeTemplate(),
    },
    [attributeMap()],
);

With the template:

html
<f-template name="my-element">
    <template>
        <p>{{greeting}}</p>
        <p>{{firstName}}</p>
    </template>
</f-template>

This registers greeting (attribute greeting, property greeting) and
firstName (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
typescript
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

html
{{text}}

Event binding

Event bindings must include the () as well as being preceeded by @ in keeping with the @microsoft/fast-element tagged template syntax.

html
<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):

html
<button @click="{handleClick($e)}"></button>

$c — execution context:

html
<button @click="{handleClick($c)}"></button>

$c.somePath — a property of the execution context (e.g. $c.parent, $c.event):

html
<button @click="{handleClick($c.parent)}"></button>

Multiple arguments:

html
<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:

html
<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's title property:

    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:

html
<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:

html
<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
{{{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 &#123; and &#125; 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:

html
<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.

shell
# 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.ts

This 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. The
playwright.declarative.webui.config.ts file sets
FAST_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.

text
/* 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 - The targetIndex: number property has been replaced by a targetId: string property. The createBehavior method no longer takes a target Node but instead takes a BehaviorTargets instance. The actual target can be looked up on the BehaviorTargets instance by indexing with the targetId property.
  • compileTemplate() - Internals have been significantly changed. The implementation no longer uses a TreeWalker. The return type has change to an HTMLTemplateCompilationResult with different properties.
  • View and HTMLView - Type parameters added to enable strongly typed views based on their data source. The constructor of HTMLView has 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, and ViewTemplate - 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 the render method 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 helper createCustomAttributePlaceholder() no longer requires an attribute name. It will be uniquely generated internally.
  • class - Bindings to class are now more nuanced. Binding directly to class will simply set the className property. If you need to bind to class knowing that manual JS will also manipulate the classList in addition to the binding, then you should now bind to :classList instead. This allows for performance optimizations in the simple, most common case.
  • Behavior and ViewBehavior - Behavior now requires an ExecutionContext for unbind. Behaviors can be used for elements or views. ViewBehavior has been introduced for use exclusively with views, and provides some optimization opportunities.
  • RefBehavior has been replaced with RefDirective. The directive also implements ViewBehavior allowing a single directive instance to be shared across all template instances that use the ref.
  • Removed SlottedBehavior and ChildrenBehavior have been replaced with SlottedDirective and ChildrenDirective. These directives allow a single directive instance to be shared across all template instances that use the ref.
  • Removed AttachedBehaviorHTMLDirective and AttachedBehaviorType since they are no longer used in the new directive/behavior architecture for ref, slotted, and children.
  • Renamed Notifier#source to Notifier#subject to align with other observable terminology and prevent name clashes with BindingObserver properties.

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

  1. Remove any runtime checks that read FAST.versions.
  2. Fix duplicate FAST installs in your bundler or dependency graph instead of relying on version tracking at runtime.
  3. Remove any fast-kernel script attributes. They no longer affect FAST initialization.

Native globals and scheduler requirements (v2 → v3)

Changed behavior

  • Browser Window runtime required: @microsoft/fast-element is a
    client-side browser runtime. It expects browser DOM, Custom Elements, Shadow
    DOM, and scheduling APIs such as requestAnimationFrame.
  • Native globalThis required: @microsoft/fast-element no longer installs
    a globalThis polyfill as a side effect.
  • No idle-callback polyfill: FAST Element v3 no longer installs or depends
    on a requestIdleCallback / cancelIdleCallback fallback. The async
    Updates queue schedules work with globalThis.requestAnimationFrame.

Migration steps

  1. Verify that the browsers you support provide native globalThis and
    requestAnimationFrame.
  2. If you still target an older browser without globalThis, load that polyfill
    before importing @microsoft/fast-element.
  3. 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 browser Window.
  4. You do not need to polyfill requestIdleCallback or cancelIdleCallback
    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

  1. Update declarative runtime imports to
    @microsoft/fast-element/declarative.js.
  2. Update declarative utility imports such as deepMerge to
    @microsoft/fast-element/declarative-utilities.js.
  3. Keep importing FAST Element APIs such as FASTElement, FAST,
    ElementController, definition/controller types, attr, Schema, and
    observable from @microsoft/fast-element.
  4. Call enableHydration() from @microsoft/fast-element/hydration.js when
    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 both
observerMap()-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:

ts
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

  1. Replace older nested helper imports with the flat path exports shown above.
  2. Remove hydration side-effect imports and call enableHydration() before FAST
    elements connect when SSR content should hydrate.
  3. 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 the
firstName 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

  1. Audit declarative templates for binding keys that contain hyphens or that
    intentionally matched attribute names exactly.
  2. If you want the v2 behavior, configure the client definition extension with
    attributeMap({ "attribute-name-strategy": "none" }).
  3. If server-rendered HTML is generated with @microsoft/fast-build, pass
    --attribute-name-strategy=none so 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 use templateOptions to delay platform
    definition or connection.
  • Elements can still be defined before a template is attached; a later
    FASTElementDefinition.template update notifies connected elements so they
    can render or hydrate with the new template.

Migration steps

  1. Remove templateOptions from element definitions.
  2. Continue calling define({ name }) when a definition needs to exist before
    its template is attached.
  3. 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

  1. Import fastElementRegistry from @microsoft/fast-element/registry.js.
  2. Use fastElementRegistry.whenRegistered(tagName) when code needs to wait for
    a definition by tag name.
  3. Use fastElementRegistry.getByType(type) or
    fastElementRegistry.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

  1. Replace manual <f-template> registration with template: 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(),
    });
  2. Replace TemplateElement.options() with definition extensions:

    typescript
    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() is imported from
    @microsoft/fast-element/attribute-map.js and observerMap() from
    @microsoft/fast-element/observer-map.js for declarative templates,
    manually supplied schemas, or both. FASTElementDefinition.schema is optional;
    declarativeTemplate() assigns it automatically, and observerMap() can
    take a manual schema with observerMap({ schema }).

  3. Replace TemplateElement.config() with enableHydration().whenHydrated(tagName)
    for tag-specific hydration waits and enableHydration().whenHydrated() for the
    active hydration batch.
    Hydration is not installed by declarative.js; call enableHydration()
    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

  1. Replace setup-only debug.js imports with an explicit enableDebug() call.
  2. Keep using the root package development export 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

  1. Replace declarative event bindings such as
    @click="{handleClick(e)}" with @click="{handleClick($e)}".
  2. Remove any TemplateParser.hasDeprecatedEventSyntax checks 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

  • attributeChangedCallback during upgrade: When isPrerendered is 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> before define() completes, so connected elements
    hydrate with a concrete template. No defer-hydration attribute is needed.
  • Binding evaluation with existing shadow root: When an existing shadow root is detected, attribute and booleanAttribute bindings skip their initial DOM update. All other binding types (event, content, property, tokenList) run normally.

New APIs

  • ElementController.isPrerendered (Promise): Resolves to true when the element had a declarative shadow root at connect time, or false when the component was client-side rendered.
  • ElementController.isHydrated (Promise): Resolves to true only when hydration ran successfully.
  • ViewController.isPrerendered (Promise | undefined): Available to custom directives. Resolves to true when the view's content was prerendered, false otherwise.
  • ViewController.isHydrated (Promise | undefined): Available to custom directives. Resolves to true only when hydration ran successfully.

Migration steps

  1. Remove HydratableElementController.install() calls.
  2. Remove import "@microsoft/fast-element/install-hydration.js" side-effect imports.
  3. Replace element.$fastController instanceof HydratableElementController checks with await element.$fastController.isPrerendered.
  4. Remove defer-hydration and needs-hydration attributes 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 return Promise<TType> only for code that
    explicitly needs to observe registration completion. When
    template: 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.
  • @customElement decorator calls define() 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

  1. Replace defineAsync() calls with define():

    typescript
    // Before
     MyElement.defineAsync({
         name: "my-element",
         templateOptions: "defer-and-hydrate",
     });
    
     // After
     MyElement.define({
         name: "my-element",
         template: declarativeTemplate(),
     });
  2. Replace subclass compose() calls that immediately register an element with
    define():

    typescript
    // Before
     MyElement.compose({
         name: "my-element",
         template,
         styles,
     }).define();
    
     // After
     MyElement.define({
         name: "my-element",
         template,
         styles,
     });
  3. Replace FASTElementDefinition.composeAsync() calls that register an element
    with subclass define():

    typescript
    // Before
    (await FASTElementDefinition.composeAsync(MyElement, {
        name: "my-element",
        template,
        styles,
    })).define();
    
    // After
    MyElement.define({
        name: "my-element",
        template,
        styles,
    });
  4. Replace registerAsync() calls with register():

    typescript
    // Before
    const el = await FASTElementDefinition.registerAsync(name);
    
    // After
    const el = await FASTElementDefinition.register(name);
  5. Replace FASTElementDefinition.compose() calls that chain .define() with
    subclass define():

    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. */
ts
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 via DOM.setBooleanAttribute (presence-based) and reads them as value !== null, so the mode: "boolean" reflection path does not route through booleanConverter.toView() or booleanConverter.fromView(). The converter methods still apply to property assignment (el.bool = X runs fromView(X)), explicit calls (booleanConverter.fromView(x)), and booleanConverter paired with mode: "reflect" (which calls toView).

Migration steps

  1. Audit boolean-mode attributes for the literal string "false" in templates or SSR HTML — the resulting property will be true, not false. To express the falsy state, omit the attribute entirely (or call element.removeAttribute(name)). Setting element.bool = false continues to remove the reflected attribute.
  2. Audit code that calls booleanConverter.toView() directly or pairs booleanConverter with mode: "reflect". The output strings are no longer "true" / "false".
  3. If you need a tri-state attribute (true / false / unset) that preserves an explicit "false" string value, use nullableBooleanConverter with mode: "reflect" instead of mode: "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

  1. Replace RenderableFASTElement(MyComponent).defineAsync({...}) with
    MyComponent.define({...}) and use declarativeTemplate() for declarative
    templates. If code explicitly observes the Promise returned by define(),
    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(),
    });
  2. Remove prepare() methods. Move any initialization logic to connectedCallback:

    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();
        }
    }
  3. Remove defer-hydration and needs-hydration attributes 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>
  4. Await $fastController.isPrerendered to detect prerendered components:

    typescript
    connectedCallback() {
        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 schemaMap on each Schema instance (private)
  • A module-level schemaRegistry export 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:

ts
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:

bash
fast build --attribute-name-strategy=none

Declarative 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:

ts
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:

ts
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.

text
/* 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`)



Sites Website Src Docs 1.X Community (`sites/website/src/docs/1.x/community.md`)



Sites Website Src Docs 1.X Components (`sites/website/src/docs/1.x/components.md`)



Sites Website Src Docs 1.X Design Systems (`sites/website/src/docs/1.x/design-systems.md`)



Sites Website Src Docs 1.X Design (`sites/website/src/docs/1.x/design.md`)



Sites Website Src Docs 1.X Fast Element (`sites/website/src/docs/1.x/fast-element.md`)



Sites Website Src Docs 1.X Integrations (`sites/website/src/docs/1.x/integrations.md`)



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.

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.

mermaid
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:#333

Example 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.

mermaid
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:#333

For 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.

mermaid
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`)



Sites Website Src Docs 1.X Tools (`sites/website/src/docs/1.x/tools.md`)



Sites Website Src Docs 2.X Advanced (`sites/website/src/docs/2.x/advanced.md`)



Sites Website Src Docs 2.X Api (`sites/website/src/docs/2.x/api.md`)



Sites Website Src Docs 2.X Community (`sites/website/src/docs/2.x/community.md`)



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

ts
npm install aurelia-fast-adapter

and then simply register it from your src/main.ts:

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:

ts
// 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:

ts
// 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.

text
/* 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:

js
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:

  • transformTaggedTemplate minifies content within tagged templates (e.g. html and css)
  • typescript allows us to write our source files in TypeScript.
  • resolve allows us to import modules installed from npm, like @microsoft/fast-element.

TypeScript

Here's an example starter tsconfig.json that you can use:

json
{
  "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:

ts
import { cssPartial } from "@microsoft/fast-element";

cssPartial`
:host {
  color: red;
}`;

2.x Example:

ts
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:

ts
import { CSSDirective } from "@microsoft/fast-element"

class MyCSSDirective extends CSSDirective {
    createCSS() {
      return "color: red;"
    }
}

export const myCssDirective = new MyCSSDirective();

2.x Example:

ts
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:

ts
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:

ts
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:

ts
import { FASTElement, customElement } from '@microsoft/fast-element';

@customElement('my-component')
export class MyComponent extends FASTElement {}

2.x Example:

ts
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`)



Sites Website Src Docs 3.X Advanced (`sites/website/src/docs/3.x/advanced.md`)



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:

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`)



Sites Website Src Docs 3.X Declarative Templates (`sites/website/src/docs/3.x/declarative-templates.md`)



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

ts
npm install aurelia-fast-adapter

and then simply register it from your src/main.ts:

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:

ts
// 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:

ts
// 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.

text
/* 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:

js
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:

  • transformTaggedTemplate minifies content within tagged templates (e.g. html and css)
  • typescript allows us to write our source files in TypeScript.
  • resolve allows us to import modules installed from npm, like @microsoft/fast-element.

TypeScript

Here's an example starter tsconfig.json that you can use:

json
{
  "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`)



Sites Website Src Docs 3.X Resources (`sites/website/src/docs/3.x/resources.md`)



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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export default abstract class Animate

Constructors

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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export default class AnimateFrom extends Animate

Properties

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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
declare class AnimateGroup

Constructors

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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
declare class AnimateSequence

Constructors

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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export default class AnimateTo extends Animate

Properties

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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export default class ScrollTrigger extends ScrollBase

Methods

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:

typescript
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:

typescript
export default class ViewEnterTrigger extends ScrollBase

Methods

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:

typescript
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:

typescript
export default class ViewExitTrigger extends ScrollBase

Methods

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:

typescript
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:

typescript
export declare function blend(mode: ColorBlendMode, bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
mode ColorBlendMode
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
export declare function blendBurn(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
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:

typescript
export declare function blendColor(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
export declare function blendDarken(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
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:

typescript
export declare function blendDodge(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
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:

typescript
export declare function blendLighten(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
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:

typescript
export declare function blendMultiply(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
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:

typescript
export declare function blendOverlay(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
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:

typescript
export declare function blendScreen(bottom: ColorRGBA64, top: ColorRGBA64): ColorRGBA64;

Parameters

Parameter Type Description
bottom ColorRGBA64
top ColorRGBA64

Returns:

ColorRGBA64

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:

typescript
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:

typescript
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:

ColorRGBA64

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:

typescript
export declare function centeredRescale(input: ColorRGBA64[], config?: CenteredRescaleConfig): ColorRGBA64[];

Parameters

Parameter Type Description
input ColorRGBA64[]
config CenteredRescaleConfig

Returns:

ColorRGBA64[]

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:

typescript
export interface CenteredRescaleConfig

Properties

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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export declare enum ColorBlendMode

Enumeration 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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export declare class ColorHSL

Constructors

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:

typescript
roundToPrecision(precision: number): ColorHSL;

Parameters

Parameter Type Description
precision number the precision to round to

Returns:

ColorHSL


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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export declare class ColorHSV

Constructors

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:

typescript
roundToPrecision(precision: number): ColorHSV;

Parameters

Parameter Type Description
precision number the precision to round to

Returns:

ColorHSV


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:

typescript
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:

typescript
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:

typescript
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:

typescript
export declare enum ColorInterpolationSpace

Enumeration 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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
export declare class ColorLAB

Constructors

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:

typescript
roundToPrecision(precision: number): ColorLAB;

Parameters

Parameter Type Description
precision number the precision to round to

Returns:

ColorLAB


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:

typescript
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:

typescript
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:

typescript
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:

typescript
equalValue(rhs: ColorLCH): boolean;

Parameters

Parameter Type Description
rhs ColorLCH the color to compare

Returns:

boolean


Sites Website Src Docs 1.X Api Fast Colors.Colorlch.Fromob