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

  1. Attach interactive root components to a descendant element (e.g. <div id="app">), never to <html> or document directly.
  2. Keep <html> and <head> statically rendered.
  3. Adjust the host page so the component comment lives under <body> or deeper.
  4. 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

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


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)