Skip to content

fix(core): respect ngSkipHydration on components with projectable nodes in LContainers#68381

Open
sonukapoor wants to merge 2 commits intoangular:mainfrom
sonukapoor:fix/ng0503-skiphy-projectable-nodes-67928
Open

fix(core): respect ngSkipHydration on components with projectable nodes in LContainers#68381
sonukapoor wants to merge 2 commits intoangular:mainfrom
sonukapoor:fix/ng0503-skiphy-projectable-nodes-67928

Conversation

@sonukapoor
Copy link
Copy Markdown
Contributor

PR Checklist

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

  • Bugfix

What is the current behavior?

Issue Number: #67928

When a component is created dynamically via ViewContainerRef.createComponent and receives projectable nodes, applying ngSkipHydration to its host element does not prevent NG0503 from being thrown during SSR serialization. The error fires even though the developer has followed the documented workaround.

What is the new behavior?

ngSkipHydration is now respected for components hosted inside an LContainer. When such a component's host element carries the ngSkipHydration attribute, serializeLContainer skips calling serializeLView for it — the same way serializeLView already skips inline child components with ngSkipHydration.

Root cause

There is an asymmetry in the serialization pipeline between inline components and dynamically created ones:

  • Inline components (Array.isArray(lView[i]) branch in serializeLView): before calling annotateHostElementForHydration, the code checks whether the host element has ngSkipHydration and bails out early if so. The component's lView is never serialized.

  • LContainer-hosted components (serializeLContainer): serializeLView was called unconditionally with no equivalent guard. When serializeLView later walked the component's projection slots and found a raw DOM node array (the projectable nodes), it threw NG0503 — regardless of whether ngSkipHydration was present.

The fix adds the missing guard in serializeLContainer: before calling serializeLView for a component lView, check whether the host element opts out of hydration, and skip if it does.

Does this PR introduce a breaking change?

  • No

Other information

A regression test was added that reproduces the original failure: a component decorated with ngSkipHydration receives a raw DOM node as a projectable node via ViewContainerRef.createComponent. Before this fix the test threw NG0503; after the fix SSR completes successfully and the component is present in the output.

…es in LContainers

When a component is created dynamically via ViewContainerRef.createComponent
and receives projectable nodes (e.g. raw DOM nodes or embedded view root nodes),
applying ngSkipHydration to its host element did not prevent NG0503 from being
thrown during SSR serialization.

The root cause is an asymmetry in the serialization pipeline. For inline child
components, serializeLView already guards the annotateHostElementForHydration
call with a ngSkipHydration attribute check, so the component's lView is never
serialized when hydration is opted out. For components hosted inside an
LContainer (created via ViewContainerRef.createComponent), serializeLContainer
called serializeLView unconditionally — bypassing that guard entirely. When
serializeLView then encountered a projection slot backed by a raw DOM node
array, it threw NG0503 regardless of the ngSkipHydration flag.

The fix adds the same guard inside serializeLContainer before calling
serializeLView: if the child lView belongs to a component whose host element
carries ngSkipHydration, the lView serialization is skipped. This matches the
existing behavior for inline components and allows the documented workaround to
actually work for dynamically created ones.

Fixes angular#67928
@pullapprove pullapprove Bot requested a review from devversion April 25, 2026 23:12
@angular-robot angular-robot Bot added the area: core Issues related to the framework runtime label Apr 25, 2026
@ngbot ngbot Bot added this to the Backlog milestone Apr 25, 2026
@JeanMeche JeanMeche requested review from thePunderWoman and removed request for devversion April 27, 2026 20:56
Comment thread packages/core/src/hydration/annotate.ts Outdated
(childHostElement as HTMLElement).hasAttribute(SKIP_HYDRATION_ATTR_NAME)
) {
// Component is skipped — nothing to serialize for its lView.
} else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we invert this logic so that we don't have an empty if block? That's much easier to follow.

Copy link
Copy Markdown
Contributor Author

@sonukapoor sonukapoor Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I thought I had done that already. I have inverted the condition. The serializeLView call now lives in the positive branch, and the empty block is gone.

Inverts the condition in serializeLContainer so the serialization call
sits in the positive branch rather than an empty true-block with the
work deferred to else. Logic is unchanged.
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