Skip to content

feat: Prerendered content binding optimization#7456

Draft
janechu wants to merge 5 commits intoreleases/fast-element-v3from
users/janechu/prerender-optimization
Draft

feat: Prerendered content binding optimization#7456
janechu wants to merge 5 commits intoreleases/fast-element-v3from
users/janechu/prerender-optimization

Conversation

@janechu
Copy link
Copy Markdown
Collaborator

@janechu janechu commented Apr 18, 2026

Pull Request

📖 Description

Replace HydratableElementController with an automatic prerendered content optimization built into ElementController. When a component connects and already has an existing shadow root (from SSR or declarative shadow DOM), the system automatically:

  • Hooks up event listeners (e.g., @click)
  • Runs structural directives (when, repeat, ref, children, slotted)
  • Subscribes to observable/attr properties for future change notifications
  • Skips re-evaluating attribute and booleanAttribute binding expressions (DOM already has correct values)

No explicit configuration from component authors is required. The presence of an existing shadow root signals prerendered content.

This is a breaking change for both @microsoft/fast-element and @microsoft/fast-html. See the migration docs in packages/fast-element/MIGRATION.md and packages/fast-html/MIGRATION.md.

👩‍💻 Reviewer Notes

Breaking changes (@microsoft/fast-element)

Removed exports:

  • HydratableElementController → use ElementController (prerendered path built in)
  • HydrationControllerCallbacks → use TemplateElement.config() in @microsoft/fast-html
  • needsHydrationAttribute → use ElementController.isPrerendered

New APIs:

  • ElementController.isPrerendered (readonly boolean) — true when an existing shadow root was detected
  • ViewController.isPrerendered (readonly boolean?) — available to directives during bind()

Changed behavior:

  • ElementController.connect() adds a template-pending guard for defineAsync() flow
  • ElementController.onAttributeChangedCallback() skips during initial upgrade when prerendered
  • HTMLBindingDirective.bind() skips attribute/booleanAttribute updateTarget calls when controller.isPrerendered
  • ViewTemplate.render() signature adds optional isPrerendered parameter

Breaking changes (@microsoft/fast-html)

Removed exports:

  • RenderableFASTElement → extend FASTElement directly

Removed concepts:

  • prepare() lifecycle hook → set state in connectedCallback
  • defer-hydration / needs-hydration attributes → automatic detection
  • waitForAncestorHydration() → not needed

Migration

See packages/fast-element/MIGRATION.md and packages/fast-html/MIGRATION.md for detailed migration steps.

Quick summary:

  1. Replace RenderableFASTElement(MyComponent).defineAsync({...}) with MyComponent.defineAsync({...})
  2. Remove HydratableElementController.install() calls
  3. Remove defer-hydration and needs-hydration from server-rendered markup
  4. Use $fastController.isPrerendered to detect prerendered components

📑 Test Plan

  • All 1392 @microsoft/fast-element tests pass
  • All 271 @microsoft/fast-html tests pass
  • New tests for isPrerendered flag detection (CSR vs DSD) and template-pending guard
  • Updated hydration lifecycle and performance metrics tests for the new flow
  • All fixture main.ts files updated to use FASTElement directly

✅ Checklist

General

  • I have included a change request file using $ npm run change
  • I have added tests for my changes.
  • I have tested my changes.
  • I have updated the project documentation to reflect my changes.
  • I have read the CONTRIBUTING documentation and followed the standards for this project.

⏭ Next Steps

  • Update @microsoft/fast-build / @microsoft/fast-ssr to stop emitting defer-hydration / needs-hydration attributes
  • Update example projects to use the new API
  • Consider adding benchmarks comparing prerendered vs CSR initial render performance

janechu and others added 5 commits April 18, 2026 01:19
Update DESIGN.md and README.md for fast-element and fast-html packages
to reflect the replacement of HydratableElementController with automatic
prerendered content detection in ElementController.

fast-element changes:
- Document isPrerendered flag on ElementController and ViewController
- Document template-pending guard in connect()
- Document attribute skip during initial upgrade for prerendered elements
- Update FASTElement lifecycle diagram with prerendered content flow
- Remove hydration.ts from package layout (consolidated into ElementController)
- Add Prerendered Content Optimization section to README

fast-html changes:
- Remove RenderableFASTElement mixin references (components extend FASTElement directly)
- Remove prepare() lifecycle hook documentation
- Remove defer-hydration and needs-hydration attribute references from markup examples
- Update lifecycle diagram to show ElementController instead of HydratableElementController
- Update integration table to reflect new ElementController hydration path
- Update RENDERING.md initial state example and hydration description
- Update RENDERING_LIFECYCLE.md Phase 5 for new prerendered content flow

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…plete

Fire hydrationComplete via queueMicrotask after the last pending
template is processed, instead of polling with requestIdleCallback.
This is simpler, more portable, and deterministic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Separate prerendered and client-side render paths into a single
if/else block instead of three separate isPrerendered checks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant