Skip to content

Commit 3b674da

Browse files
authored
[Blazor] Fix Blazor navigation error caused by metadata comments in logical tree
Prevent metadata comments from becoming logical elements during rendering ## Description This PR fixes a bug where Blazor metadata comments (such as WebAssembly options, component state, and initializers) were being converted into logical elements during the initial render. When these comments were later removed from the DOM during component discovery, they remained in the logical tree as orphaned references, causing `insertBefore` errors during subsequent navigation between different render modes. The fix modifies `toLogicalElement()` in `LogicalElements.ts` to skip metadata comments before they enter the logical tree, using a new `isMetadataComment()` helper function in `ComponentDescriptorDiscovery.ts` that identifies all Blazor metadata comment types. Fixes #64522, #64472 ## Customer Impact Users experience JavaScript errors and navigation failures when navigating between pages with different render modes (InteractiveServer, InteractiveWebAssembly, or InteractiveAuto) in Blazor Web Apps. The error manifests as: - `Uncaught TypeError: Cannot read properties of null (reading 'insertBefore')` - Failed page transitions requiring full browser refresh - Broken user experience in production applications This particularly affects applications using: - `PageTitle` components with different render modes - Navigation between InteractiveWebAssembly and InteractiveServer pages - Any scenario mixing render modes in a Blazor United application ## Regression? - [x] Yes - [ ] No This is a regression introduced in .NET 10.0 when the WebAssembly options discovery mechanism was added. The `<!--Blazor-WebAssembly:{...}-->` comment is new to v10.0 and was not present in v9.0. The `toLogicalElement()` function was indiscriminately converting all nodes including these new metadata comments into logical elements, which caused the orphaning issue when the comments were removed from the DOM. ## Risk - [ ] High - [ ] Medium - [x] Low **Justification:** - The fix is surgical and only affects the initial logical tree construction - It prevents a specific category of nodes (metadata comments) from entering the logical tree - Metadata comments were never intended to be logical elements - they are consumed during discovery - The fix aligns behavior with design intent: these comments are transient markers, not components - Extensively validated with two independent reproduction cases from separate bug reports - No changes to core rendering logic, only to node filtering during tree construction - The `isMetadataComment()` function uses explicit prefix matching with well-defined comment formats ## Verification - [x] Manual (required) - [ ] Automated **Manual verification completed:** 1. **BlazorApp10** (Issue #64522): Validated navigation between InteractiveServer pages with/without PageTitle components, and InteractiveWebAssembly pages. Multiple navigation cycles completed without errors. 2. **BugReportApp** (Issue #64472): Validated navigation between InteractiveWebAssembly → InteractiveServer → InteractiveWebAssembly. Both directions work correctly with SignalR connections established properly. ## Packaging changes reviewed? - [ ] Yes - [ ] No - [x] N/A This is a JavaScript/TypeScript code change only. No packaging changes required. ---- ## When servicing release/2.3 - [ ] Make necessary changes in eng/PatchConfig.props
1 parent d5aa736 commit 3b674da

File tree

2 files changed

+20
-1
lines changed

2 files changed

+20
-1
lines changed

src/Components/Web.JS/src/Rendering/LogicalElements.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
import { ComponentDescriptor } from '../Services/ComponentDescriptorDiscovery';
4+
import { ComponentDescriptor, isMetadataComment } from '../Services/ComponentDescriptorDiscovery';
55

66
/*
77
A LogicalElement plays the same role as an Element instance from the point of view of the
@@ -109,6 +109,12 @@ export function toLogicalElement(element: Node, allowExistingContents?: boolean)
109109
}
110110

111111
element.childNodes.forEach(child => {
112+
// Skip metadata comments that will be consumed during discovery
113+
// These are not components and should not be part of the logical tree
114+
if (isMetadataComment(child)) {
115+
return;
116+
}
117+
112118
const childLogicalElement = toLogicalElement(child, /* allowExistingContents */ true);
113119
childLogicalElement[logicalParentPropname] = element;
114120
childrenArray.push(childLogicalElement);

src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
// Metadata comments are consumed during discovery and should not be part of the logical tree.
5+
// They include: WebAssembly options, component state, and web initializers.
6+
export function isMetadataComment(node: Node): boolean {
7+
if (node.nodeType !== Node.COMMENT_NODE) {
8+
return false;
9+
}
10+
const content = node.textContent || '';
11+
return content.trim().startsWith('Blazor-Server-Component-State:') ||
12+
content.trim().startsWith('Blazor-WebAssembly-Component-State:') ||
13+
content.trim().startsWith('Blazor-Web-Initializers:') ||
14+
content.trim().startsWith('Blazor-WebAssembly:');
15+
}
16+
417
export function discoverComponents(root: Node, type: 'webassembly' | 'server' | 'auto'): ComponentDescriptor[] {
518
switch (type) {
619
case 'webassembly':

0 commit comments

Comments
 (0)