dotnet/aspnetcore · error

The ' ' selector is not supported.

Error message

The '${beforeElementSelector}' selector is not supported.

What it means

Thrown by attachRootComponentToElement when the element selector ends with '::before'. Blazor supports '::after' to append content after an element, but '::before' is explicitly rejected because appending into a ::before pseudo-position is not meaningful for component rendering. Only ::after is supported as a positional suffix.

Solutions

  1. Remove the '::before' suffix, or switch to '::after' if you need appended content.
  2. Use a plain CSS selector for the host element and structure the DOM so the component lands where needed.
  3. Add a real child element as an insertion point instead of a pseudo-element.

Example fix

// before (throws)
attachRootComponentToElement('#host::before', componentId, browserRendererId);

// after
attachRootComponentToElement('#host', componentId, browserRendererId);
// or append after the host
attachRootComponentToElement('#host::after', componentId, browserRendererId);
Defensive patterns

Strategy: validation

Validate before calling

if (selector.endsWith('::before')) {
  throw new Error('::before selectors are unsupported; use ::after or a plain selector.');
}
attachRootComponentToElement(selector, componentId, browserRendererId);

Type guard

function isSupportedSelector(sel: string): boolean {
  return !sel.endsWith('::before');
}

Prevention

When it happens

Trigger: Calling attachRootComponentToElement (or configuring a root component selector) with a string ending in '::before', e.g. '#host::before'.

Common situations: Developer assumes ::before and ::after are symmetric and tries ::before; a typo; copying CSS pseudo-element intuition into Blazor selectors.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/31328c14c3122ab3. Report an issue: GitHub.

Appendix: source

Thrown at src/Components/Web.JS/src/Rendering/Renderer.ts:43

  let browserRenderer = browserRenderers[browserRendererId];
  if (!browserRenderer) {
    browserRenderer = new BrowserRenderer(browserRendererId);
    browserRenderers[browserRendererId] = browserRenderer;
  }

  browserRenderer.attachRootComponentToLogicalElement(componentId, logicalElement, appendContent);
}

export function attachRootComponentToElement(elementSelector: string, componentId: number, browserRendererId: number): void {
  const afterElementSelector = '::after';
  const beforeElementSelector = '::before';
  let appendContent = false;

  if (elementSelector.endsWith(afterElementSelector)) {
    elementSelector = elementSelector.slice(0, -afterElementSelector.length);
    appendContent = true;
  } else if (elementSelector.endsWith(beforeElementSelector)) {
    throw new Error(`The '${beforeElementSelector}' selector is not supported.`);
  }

  const element = getAndRemovePendingRootComponentContainer(elementSelector)
    || document.querySelector(elementSelector);
  if (!element) {
    throw new Error(`Could not find any element matching selector '${elementSelector}'.`);
  }

  // 'allowExistingContents' to keep any prerendered content until we do the first client-side render
  // Only client-side Blazor supplies a browser renderer ID
  attachRootComponentToLogicalElement(browserRendererId, toLogicalElement(element, /* allow existing contents */ true), componentId, appendContent);
}

export function getRendererer(browserRendererId: number): BrowserRenderer | undefined {
  return browserRenderers[browserRendererId];
}

export function renderBatch(browserRendererId: number, batch: RenderBatch): void {

View on GitHub (pinned to 3600ca084e)