You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: add isHydrated promise and fix isPrerendered semantics
Split isPrerendered into two separate promises:
- isPrerendered: resolves true when DSD shadow root existed at connect
- isHydrated: resolves true only when hydration actually ran
Fix templates.html formatting that broke declarative bindings.
Update DESIGN.md, README.md, DECLARATIVE_RENDERING_LIFECYCLE.md,
migration guide, and lifecycle-callbacks docs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: packages/fast-element/DECLARATIVE_RENDERING_LIFECYCLE.md
+4-3Lines changed: 4 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -96,10 +96,11 @@ Once the template is attached to the partial definition, the element completes i
96
96
When custom elements are instantiated in the DOM, the following occurs:
97
97
98
98
1.**Element Creation**: The platform creates instances of the custom element
99
-
2.**Prerendered Content Detection**: `ElementController` detects the existing shadow root from SSR and sets `isPrerendered = true`
100
-
3.**Concrete Template Ready**: Because `declarativeTemplate()` resolved during
99
+
2.**Prerendered Content Detection**: `ElementController` detects the existing shadow root from SSR — `isPrerendered` resolves `true`
100
+
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.
101
+
4.**Concrete Template Ready**: Because `declarativeTemplate()` resolved during
101
102
definition, `connect()` starts with the final template already attached.
102
-
4.**Hydration**: `ElementController` uses `template.hydrate()` to create a
103
+
5.**Hydration**: `ElementController` uses `template.hydrate()` to create a
103
104
`HydrationView` that maps existing DOM nodes to binding targets using `fe:b`
Copy file name to clipboardExpand all lines: packages/fast-element/DESIGN.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -93,7 +93,7 @@ registry.
93
93
- Extends `PropertyChangeNotifier` so the element itself participates in the observable system.
94
94
- Holds the element's `FASTElementDefinition` (name, template, styles, observed attributes).
95
95
- Manages a `Stages` state machine: `disconnected → connecting → connected → disconnecting → disconnected`.
96
-
- Exposes `isPrerendered: Promise<boolean>` which resolves to `true`after prerendered content has been hydrated, or `false` when the component is client-side rendered. The `ViewController` interface also exposes `isPrerendered` as `Promise<boolean>` for custom directives. Attribute-skip logic during the hydration bind uses an internal `_skipAttrUpdates` flag that is never exposed as a public boolean.
96
+
- Exposes `isPrerendered: Promise<boolean>` which resolves to `true`when the element had a declarative shadow root (DSD) at connect time, regardless of whether hydration ran. Exposes `isHydrated: Promise<boolean>` which resolves to `true` only when hydration actually ran successfully. The `ViewController` interface also exposes both `isPrerendered` and `isHydrated` as `Promise<boolean>` for custom directives. Attribute-skip logic during the hydration bind uses an internal `_skipAttrUpdates` flag that is never exposed as a public boolean.
97
97
- On `connect()`: restores pre-upgrade observable values, calls `connectedCallback` on all `HostBehavior`s, renders the current template into the shadow root when one is available, and applies styles.
98
98
- Rendering is split into two modular paths via `renderPrerendered()` and `renderClientSide()`:
99
99
-**Prerendered**: `renderPrerendered()` is only reachable when hydration has been explicitly enabled via `enableHydration()`. It registers the element in the static hydration tracker, fires the definition's `elementWillHydrate` callback, swaps `onAttributeChangedCallback` to a no-op so the upgrade-time burst of callbacks is discarded, hydrates the existing DOM via `template.hydrate()`, fires `elementDidHydrate`, then restores the standard handler and removes the element from the tracker. The entire method is wrapped in `try/finally` to guarantee cleanup even if an error occurs during hydration. After this point, all future attribute changes flow through the real handler with zero overhead.
Copy file name to clipboardExpand all lines: packages/fast-element/README.md
+15-8Lines changed: 15 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -140,7 +140,12 @@ enableHydration({
140
140
});
141
141
```
142
142
143
-
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. The `isPrerendered` property on the controller is a `Promise<boolean>` that resolves to `true` after prerendered content has been hydrated, or `false` when the component is client-side rendered. This enables several optimizations:
143
+
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:
144
+
145
+
-**`isPrerendered: Promise<boolean>`** — resolves `true` when the element had a declarative shadow root (DSD) at connect time, regardless of whether hydration ran.
146
+
-**`isHydrated: Promise<boolean>`** — resolves `true` only when hydration actually ran successfully.
147
+
148
+
This enables several optimizations:
144
149
145
150
-**Hydration instead of re-render**: The template uses `hydrate()` to map existing DOM nodes to binding targets rather than cloning new DOM.
146
151
-**Declarative template resolution**: `declarativeTemplate()` waits for the
@@ -167,17 +172,19 @@ MyComponent.define({
167
172
});
168
173
```
169
174
170
-
Component authors can await the promise to know when hydration is complete:
175
+
Component authors can await both promises to distinguish prerendered content from successful hydration:
// Had DSD but hydration wasn't enabled — client-side rendered
184
+
}
178
185
```
179
186
180
-
Custom directives can also await `controller.isPrerendered`(a`Promise<boolean>` on the `ViewController` interface) to determine whether the view's content was prerendered.
187
+
Custom directives can also await `controller.isPrerendered`and `controller.isHydrated` (both`Promise<boolean>` on the `ViewController` interface) to determine how the view's content was rendered.
Copy file name to clipboardExpand all lines: packages/fast-element/test/declarative/fixtures/ecosystem/declarative-no-hydration/declarative-no-hydration.spec.ts
0 commit comments