angular/components · error

<svg> tag not found

Error message

<svg> tag not found

What it means

MatIconRegistry._svgElementFromString parses an SVG literal string by assigning it to a DIV's innerHTML and querying for an 'svg' element. If the string contains no <svg> tag at its root, the registry cannot produce an icon element and throws. This is a guard against malformed icon literals registered via addSvgIconLiteral or addSvgIconLiteralInNamespace.

Source

Thrown at src/material/icon/icon-registry.ts:579

    // http://stackoverflow.com/questions/23003278/svg-innerhtml-in-firefox-can-not-display
    const svg = this._svgElementFromString(trustedHTMLFromString('<svg></svg>'));
    // Clone the node so we don't remove it from the parent icon set element.
    svg.appendChild(iconElement);

    return this._setSvgAttributes(svg, options);
  }

  /**
   * Creates a DOM element from the given SVG string.
   */
  private _svgElementFromString(str: TrustedHTML): SVGElement {
    const div = this._document.createElement('DIV');
    div.innerHTML = str as unknown as string;
    const svg = div.querySelector('svg') as SVGElement;

    // TODO: add an ngDevMode check
    if (!svg) {
      throw Error('<svg> tag not found');
    }

    return svg;
  }

  /**
   * Converts an element into an SVG node by cloning all of its children.
   */
  private _toSvgElement(element: Element): SVGElement {
    const svg = this._svgElementFromString(trustedHTMLFromString('<svg></svg>'));
    const attributes = element.attributes;

    // Copy over all the attributes from the `symbol` to the new SVG, except the id.
    for (let i = 0; i < attributes.length; i++) {
      const {name, value} = attributes[i];

      if (name !== 'id') {
        svg.setAttribute(name, value);

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Wrap the literal in a root <svg> element including the xmlns attribute: <svg xmlns="http://www.w3.org/2000/svg" viewBox="...">...</svg>
  2. Verify the string actually contains '<svg' before registering (log it in dev).
  3. If you meant to load from a URL/file, use addSvgIcon(name, sanitizer.bypassSecurityTrustResourceUrl(url)) instead of the literal variant.

Example fix

// before
registry.addSvgIconLiteral('star', '<path d="M3 3h18v18H3z"/>');
// after
registry.addSvgIconLiteral('star', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 3h18v18H3z"/></svg>');
Defensive patterns

Strategy: validation

Validate before calling

function isSvgLiteral(str: string): boolean {
  return typeof str === 'string' && /<svg[\s>]/i.test(str.trim());
}
if (!isSvgLiteral(svgMarkup)) {
  throw new Error('Icon literal must contain a root <svg> element');
}
registry.addSvgIconLiteral('star', svgMarkup);

Type guard

function hasRootSvg(str: unknown): str is string {
  return typeof str === 'string' && new DOMParser()
    .parseFromString(str, 'image/svg+xml')
    .querySelector('svg') !== null;
}

Try / catch

try {
  registry.addSvgIconLiteral(name, markup);
} catch (e) {
  if ((e as Error).message.includes('<svg> tag not found')) {
    console.warn(`Skipping icon "${name}": markup has no <svg> root`);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling registry.addSvgIconLiteral(name, html) or addSvgIconLiteralInNamespace(ns, name, html) with a string whose root is not an <svg> element (e.g. a <path>, <img>, empty string, or HTML wrapper).

Common situations: Copying an SVG path's inner markup instead of the whole <svg>...</svg> element; build pipelines that strip the svg tag; passing an asset URL string instead of inline SVG markup.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/f6e30b2828e568d3. Report an issue: GitHub.