dotnet/aspnetcore · critical · Error
Root components cannot be marked as interactive. The
Error message
Root components cannot be marked as interactive. The <html> element must be rendered statically so that scripts are not evaluated multiple times.
What it means
Thrown by assertNotDirectlyOnDocument when a component marker's parent node is the Document itself (i.e. the marker is a direct child of the document root, such as attached to <html>). Root components attached directly to the document would make <html> interactive, causing scripts to evaluate multiple times. The guard forbids interactive root components on the document root.
Solutions
- Attach interactive root components to a descendant element (e.g. <div id="app">), never to <html> or document directly.
- Keep <html> and <head> statically rendered.
- Adjust the host page so the component comment lives under <body> or deeper.
- If global interactivity is needed, scope it to <body> rather than document.
Defensive patterns
Strategy: validation
Validate before calling
function assertNotDocumentRoot(marker: Node) {
if (marker.parentNode instanceof Document || marker.parentNode === document.documentElement) {
throw new Error('Do not attach interactive root components to <html> or document.');
}
} Prevention
- Always scope interactive components under <body> or deeper elements.
- Keep <html> and <head> statically rendered.
- Review App.razor/_Host to ensure the component host is a body descendant.
When it happens
Trigger: Placing a Blazor interactive root component marker such that its parent is document; rendering a component whose host is the <html> element; SSR output that puts the component comment directly under document.
Common situations: Trying to make the entire page (including <head> scripts) interactive; misconfigured App.razor/_Host that attaches the root at document level; a custom layout that targets document.documentElement.
Related errors
- Could not find an end component comment for
- Found malformed component comment at
- assembly must be defined when using a descriptor.
- descriptor must be defined when using a descriptor.
- End of component comment must have a value for the…
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/98f58293f258a91f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts:181
} else {
return;
}
}
}
function parseCommentPayload(json: string): ServerComponentMarker | WebAssemblyComponentMarker | AutoComponentMarker {
const payload = JSON.parse(json);
const { type } = payload;
if (type !== 'server' && type !== 'webassembly' && type !== 'auto') {
throw new Error(`Invalid component type '${type}'.`);
}
return payload;
}
function assertNotDirectlyOnDocument(marker: Node) {
if (marker.parentNode instanceof Document) {
throw new Error('Root components cannot be marked as interactive. The <html> element must be rendered statically so that scripts are not evaluated multiple times.');
}
}
function getComponentEndComment(payload: ComponentMarker, start: Comment, iterator: ComponentCommentIterator): Comment | undefined {
const { prerenderId } = payload;
if (!prerenderId) {
return undefined;
}
while (iterator.next() && iterator.currentElement) {
const node = iterator.currentElement;
if (node.nodeType !== Node.COMMENT_NODE) {
continue;
}
if (!node.textContent) {
continue;
}
View on GitHub (pinned to 3600ca084e)