Skip to content

fix(core): don't re-apply protected attributes during hydration - #70729

Closed
arturovt wants to merge 1 commit into
angular:mainfrom
arturovt:fix/hydration-protected-elements
Closed

arturovt wants to merge 1 commit into
angular:mainfrom
arturovt:fix/hydration-protected-elements

Conversation

@arturovt

@arturovt arturovt commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Some elements reload their resource whenever one of their attributes is
set again, even to the exact same value. For example, an
<iframe src="..."> that already has its src set on the server will
make the browser reload the same page a second time when Angular
hydrates it, because hydration re-applies the element's static
attributes on the client. This causes a visible flicker and an extra,
unnecessary network request. The same thing happens with
<embed src="..."> and <object data="...">.

To fix this without adding any cost to apps that don't use these
elements, the server now records which of these attributes are already
correctly set on an element, and includes that information in the
hydration data it sends to the client. During hydration, the client
uses this information to skip re-setting the attribute instead of
setting it again with the same value. Apps that don't hydrate, or
elements that aren't affected, go through the exact same code as
before.

The lookup itself lives behind the existing hydration-only element
implementation (enabled via enableLocateOrCreateElementNodeImpl), not
in the shared element-creation code all apps go through, so it tree-
shakes out of apps that never enable hydration. It hands its result to
the shared code through a small module-level flag, the same way
wasLastNodeCreated already does.

This continues the approach discussed in #52478, where re-checking
attribute values at runtime on every element turned out to be too
costly to justify. Serializing the information on the server once and
reading it back during hydration keeps the normal, non-hydration code
path completely untouched.

This only covers static attributes for now. Dynamic bindings (e.g.
<iframe [src]="url">) still get re-applied during hydration and will
be addressed in a follow-up.

Related to #52477

@angular-robot angular-robot Bot added the area: core Issues related to the framework runtime label Sep 15, 2026
@ngbot ngbot Bot added this to the Backlog milestone Sep 15, 2026
@arturovt

arturovt commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor Author

@thePunderWoman this is a followup to #52478, which got a solid design out of the thread with Andrew K and Alex (related to #52477) but ended up stalling and got closed for staleness before it landed

@arturovt
arturovt marked this pull request as ready for review September 15, 2026 09:07
@pullapprove
pullapprove Bot requested a review from JeanMeche September 15, 2026 09:07
Some elements reload their resource whenever one of their attributes is
set again, even to the exact same value. For example, an
`<iframe src="...">` that already has its `src` set on the server will
make the browser reload the same page a second time when Angular
hydrates it, because hydration re-applies the element's static
attributes on the client. This causes a visible flicker and an extra,
unnecessary network request. The same thing happens with
`<embed src="...">` and `<object data="...">`.

To fix this without adding any cost to apps that don't use these
elements, the server now records which of these attributes are already
correctly set on an element, and includes that information in the
hydration data it sends to the client. During hydration, the client
uses this information to skip re-setting the attribute instead of
setting it again with the same value. Apps that don't hydrate, or
elements that aren't affected, go through the exact same code as
before.

The lookup itself lives behind the existing hydration-only element
implementation (enabled via enableLocateOrCreateElementNodeImpl), not
in the shared element-creation code all apps go through, so it tree-
shakes out of apps that never enable hydration. It hands its result to
the shared code through a small module-level flag, the same way
wasLastNodeCreated already does.

This continues the approach discussed in angular#52478, where re-checking
attribute values at runtime on every element turned out to be too
costly to justify. Serializing the information on the server once and
reading it back during hydration keeps the normal, non-hydration code
path completely untouched.

This only covers static attributes for now. Dynamic bindings (e.g.
`<iframe [src]="url">`) still get re-applied during hydration and will
be addressed in a follow-up.

Related to angular#52477
@arturovt
arturovt force-pushed the fix/hydration-protected-elements branch from ea72570 to fc1b44d Compare September 22, 2026 19:22
@JeanMeche

Copy link
Copy Markdown
Member

Thanks for putting this together @arturovt!

Closing this as superseded by #71200, which skips setupStaticAttributes altogether for hydrated elements (!wasLastNodeCreated()). That avoids re-setting static attributes on <iframe src="..."> / <embed src="..."> / <object data="..."> (as well as other static attributes, classes, and styles) during hydration without needing ngh serialization or extra runtime state.

Dynamic bindings (e.g. <iframe [src]="url">, #52477) remain separate and can still be addressed in a follow-up.

@JeanMeche JeanMeche closed this Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Issues related to the framework runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants