fast icon indicating copy to clipboard operation
fast copied to clipboard

🚀 Major Release Proposal: `@microsoft/fast-element 3.0.0`

Open janechu opened this issue 3 months ago • 3 comments

🚀 Major Release Proposal: @microsoft/fast-element 3.0.0

This issue outlines proposed changes for the next major version bump to 3.0.0 of @microsoft/fast-element, this release also includes proposed changes that would result in the integration of @microsoft/fast-html into @microsoft/fast-element.


Proposed Changes

Declarative Template Integration

The @microsoft/fast-html package provides a declarative templating language for FASTElement custom elements. This should be standardized and included with the @microsoft/fast-element package as a tree-shakable export.

For more information on our position to include declarative templating into @microsoft/fast-element you can refer to our proposal on Dynamic DSD.

Observer Map

In addition to the declarative templates, the Observer Map in @microsoft/fast-html is an automated observation utility that when paired with declarative templates deduces from the template paths what the general data structure is and applies observer patterns to them. This will also be moved to @microsoft/fast-element as an export for increased developer experience.

Declarative Shadow DOM

Usage of DSD (Declarative Shadow DOM) will be leveraged more directly in @microsoft/fast-element.

  • Defering hydration should be checked in JavaScript and not rely on an attribute

    The current use of defer-hydration as an attribute that is then leveraged by the hydratable element controller appears unnecessary and relies on knowledge from the renderer of the DSD. Given that the DSD may be rendered with no knowledge of the internal workings of the custom element class, for example from a non-JS backend, the implementation should not make that assumption.

  • Whether a component needs hydration should be determined by the presence of a Declarative Shadow DOM

    Detect shadowRoot directly on the custom element, we are currently relying on ElementInternals. At render cycle start, check for an existing shadowRoot before calling attachShadow(). If a DSD exists and a FASTElement extended class is used for the custom element, it should always be assumed that the component is hydratable.

  • Investigate alternatives to inline comments/datasets

    The current implementation uses inline comments and datasets to indicate the location of bindings for rationalization with a template. This adds significant time to FCP. As part of the major version bump investigations will be taken to identify areas of improvement and where possible the removal of as many markers as possible.


Improvements

  • Empty custom element class generation

    • If a component has no class but a declarative template has been defined, generate an empty one so new components can take advantage of the presentational elements of the template.
  • Lifecycle hooks

    Determine with async compositing and definitions what lifecycle hooks may be beneficial. This should assist in hydration and have other potential benefits for developers.

  • isConnected as a promise (@radium-v)

    • This change is needed for proper children resolution.
  • Styling ergonomics (@marchbox)

    Improve developer experience for applying styles, with the inclusion of CSS Modules (Proposal, Firefox/Safari Intent to Ship) as well as usage of @sheet with examples to the documentation on best practices.

  • Filters in custom element class (@marchbox)

    Move filters from template to class for cleaner declarative syntax.

  • Event binding improvements (@radium-v)

    • Remove automatic preventDefault from event bindings.
    • Allow specifying event binding capture (e.g. invalid event in field component).
  • Async methods

    Async methods have recently been included to allow integration with @microsoft/fast-html due to definitions not being considered complete until a template has been assigned. The async methods should replace the current synchronous methods. These methods are:

    • define → register with custom element registry
    • compose → compose from class, template, styles
    • register → register class prototype (template/styles/shadow options can be set later)
    • from → derive from a base class

    Order of operations:

    1. register custom element class
    2. compose definition
    3. define/from register with custom element registry
    • Clarify connected callback timing (e.g. async definition of menu-item vs menu)

Browser/TypeScript API Alignment

  • Scoped custom element registries

    • Explore utilities/options in TemplateElement for declarative templates.
  • ElementInternals consistency (@radium-v)

  • SVG templating (@davatron5000)

    Current issue: partials using SVG elements (e.g. <path>) lose namespace context, these items are assumed to be in the regular HTML namespace

  • Simplify tsconfig/integration (@radium-v)

    • Document/remove useDefineClassFields, sideEffects.
    • Remove experimental decorators.
    • Prefer usage of Object.assign.
  • Slotting and children parity (@radium-v)


Documentation

  • Update queuing behavior

    • Clarify usage of Updates.enqueue
    • Explore better solutions (dependency injection, dependency trees)
    • Related: Fluent UI Issue #33589
    • Components to review: Menu and other nested components
  • State tracking

    • The current recommendation is to use Dependency Injection, this needs documented examples for developers.
    • There appears to be some state tracking mechanisms in place, however these are not well documented and appear to have performance issues. Determine if they should be removed in favor of DI.

Contributors

  • @janechu
  • @chrisdholt
  • @radium-v
  • @KingOfTac
  • @marchbox
  • @cosorgen
  • @davatron5000
  • @nicholasrice

Next Steps

  • Review and prioritize issues in milestones and project tracking
  • Assign owners for issues
  • Begin moving any files prior to approaching work for ease of tracking changes.

janechu avatar Nov 14 '25 23:11 janechu

isConnected as a promise

This should probably be named as whenConnected because isConnected is already a property on Node, and whenConnected is aligned with whenDefined on CustomElementRegistry interface.

marchbox avatar Nov 18 '25 22:11 marchbox

isConnected as a promise

This should probably be named as whenConnected because isConnected is already a property on Node, and whenConnected is aligned with whenDefined on CustomElementRegistry interface.

In the ElementController we have an isConnected, @radium-v as this was your suggestion, can you clarify if that was the intent, to make the isConnected in ElementController a promise?

janechu avatar Nov 19 '25 21:11 janechu

@janechu the isConnected (both on the ElementController and Node.isConnected are boolean values. It would be great to have an async way to defer certain actions (usually triggered by @attr or @observable) until the element has been and/or currently is connected.

It would remove the need for checks in -Changed() methods that are intended to be deferred until the element is connected, but the check happens usually during construction or instantiation once and never happens again:

class MyElement extends FASTElement {
  @attr({ mode: "boolean" });
  something: boolean = true;

  somethingChanged(prev, next) {
    if (this.$fastController.isConnected) {
      if (next) {
        this.doSomething();
      }
    }
  }
}

The somethingChanged() observable method gets called immediately during construction because something is given the initial value of true. But because it may not be connected yet, the method fails the first condition and returns. The only way to re-trigger the observable on connection is to explicitly call somethingChanged() in the connectedCallback(), or to change the value to false and back to true after the element is connected.

A promised value would resolve once isConnected is true, allowing the rest of the method to be run at a more relevant time. What I'm thinking is some sort of this.whenConnected() promise, similar to customElements.whenDefined().

radium-v avatar Nov 19 '25 22:11 radium-v