dotnet/aspnetcore · error · Error

Unable to focus an SVG element that does not have a tabindex

Error message

Unable to focus an SVG element that does not have a tabindex.

What it means

Thrown by domFunctions.focus when the target is an SVGElement without a tabindex attribute. Unlike HTMLElement, SVG elements are not focusable by default; calling .focus() on one without an explicit tabindex has no effect and is almost always a bug, so Blazor refuses rather than silently no-op.

Source

Thrown at src/Components/Web.JS/src/DomWrapper.ts:18

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import '@microsoft/dotnet-js-interop';

export const domFunctions = {
  focus,
  focusBySelector,
};

function focus(element: HTMLOrSVGElement, preventScroll: boolean): void {
  if (element instanceof HTMLElement) {
    element.focus({ preventScroll });
  } else if (element instanceof SVGElement) {
    if (element.hasAttribute('tabindex')) {
      element.focus({ preventScroll });
    } else {
      throw new Error('Unable to focus an SVG element that does not have a tabindex.');
    }
  } else {
    throw new Error('Unable to focus an invalid element.');
  }
}

function focusBySelector(selector: string) {
  const element = document.querySelector(selector) as HTMLElement;
  if (element) {
    // If no explicit tabindex is defined, mark it as programmatically-focusable.
    // This does actually add a new HTML attribute, but it shouldn't interfere with
    // diffing because diffing only deals with the attributes you have in your code.
    if (!element.hasAttribute('tabindex')) {
      element.tabIndex = -1;
    }

    element.focus({ preventScroll: true });
  }

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Add tabindex="-1" (or tabindex="0") to the SVG element before focusing it.
  2. Wrap the SVG in a focusable container (e.g. a <button>) and focus that instead.
  3. Use focusBySelector with a selector that targets an HTMLElement ancestor.
  4. Guard the call: if (element instanceof SVGElement && !element.hasAttribute('tabindex')) return;

Example fix

// before
<svg #icon></svg>
await icon.FocusAsync(); // throws

// after
<svg #icon tabindex="-1"></svg>
await icon.FocusAsync();
Defensive patterns

Strategy: type-guard

Validate before calling

function focusIfFocusable(el: HTMLOrSVGElement | null, preventScroll = false) {
  if (el instanceof SVGElement && !el.hasAttribute('tabindex')) return;
  if (el instanceof HTMLElement || el instanceof SVGElement) {
    el.focus({ preventScroll });
  }
}

Type guard

function isFocusable(el: HTMLOrSVGElement | null): el is HTMLElement | SVGElement {
  if (el instanceof HTMLElement) return true;
  if (el instanceof SVGElement) return el.hasAttribute('tabindex');
  return false;
}

Try / catch

try {
  Blazor._internal.domFunctions.focus(svgEl, false);
} catch (e) {
  if (/does not have a tabindex/.test((e as Error).message)) {
    svgEl.setAttribute('tabindex', '-1');
    svgEl.focus({ preventScroll: false });
  } else throw e;
}

Prevention

When it happens

Trigger: A @focus directive or Blazor._internal.domFunctions.focus() call targeting an SVG element (e.g. <svg>, <rect>, <circle>, <path>) that has no tabindex attribute. Common when a component tries to programmatically focus an icon or vector graphic on validation error.

Common situations: Calling FocusAsync on an ElementReference pointing at an SVG; routing focus to an SVG icon used as a button without making it focusable; accessibility tooling that walks the DOM and focuses SVG nodes.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/cd1e374ff15e1bbe. Report an issue: GitHub.