dotnet/aspnetcore · error
Root components cannot be marked as interactive. The <html>
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 (ComponentDescriptorDiscovery.ts:181) when a component marker's direct parent is the Document node itself — i.e. someone tried to make the <html> root element an interactive (Server/WebAssembly/Auto) component. Blazor requires the root <html> to remain static SSR so that <script> tags are not re-evaluated when interactive rendering re-renders the tree, which would double-load scripts and corrupt state.
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 294cab2f9b)
Solutions
- Keep rendermode attributes off the root document layout; apply @rendermode only to nested components below <html>/<head>/<body>.
- Render the app shell statically (SSR) and mark individual routable components or sub-trees as interactive.
- Review App.razor / Routes.razor and ensure InteractiveServer/InteractiveAuto is on the Router content, not on a component that owns <html>.
- If you need interactive head content, use <HeadOutlet> with an appropriate rendermode rather than re-rendering <html>.
Example fix
<!-- before: App.razor root marked interactive -->
<!DOCTYPE html>
<html lang="en">
<head><component type="typeof(App)" render-mode="InteractiveServer" /></head>
<!-- after: static shell, interactive router inside -->
<!DOCTYPE html>
<html lang="en">
<head><HeadOutlet /></head>
<body>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
</body>
</html> Defensive patterns
Strategy: validation
Validate before calling
function assertRootIsStatic(rootComponentType: string, renderMode?: string) {
if (renderMode && renderMode !== 'StaticServer' && rootComponentType === 'html') {
throw new Error('Do not apply an interactive rendermode to the document root.');
}
} Type guard
function isSafeRenderModeForRoot(mode: string | undefined): boolean {
return !mode || mode === 'StaticServer';
} Prevention
- Keep rendermode attributes off <html>/document-level components.
- Apply interactive rendermodes only to nested routable components.
- Review App.razor / Routes.razor in code review.
When it happens
Trigger: Registering a root component whose Razor output targets the document/html element with an interactive rendermode; App.razor or _Host equivalent placing an InteractiveServer/InteractiveAuto rendermode attribute such that the marker's parent resolves to Document; misconfigured Routes.razor with a rendermode on a component that wraps <html>.
Common situations: Migrating a legacy Blazor Server _Host.cshtml and accidentally applying @rendermode InteractiveServer at the document root; putting <HeadOutlet> or root components in a way that the top-level marker lands directly under document; custom IComponentRenderMode assignments on root components.
Related errors
- Dynamic root components have already been enabled.
- Found malformed component comment at ${candidateStart.textCo
- Invalid component type '${type}'.
- Could not find an end component comment for '${start}'.
- Invalid end of component comment: '${json}'
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/98f58293f258a91f.
Report an issue: GitHub.