angular/components · warning

Detected a matBadge on an "aria-hidden" "<mat-icon>". Consid

Error message

Detected a matBadge on an "aria-hidden" "<mat-icon>". Consider setting aria-hidden="false" in order to surface the information assistive technology.

What it means

A `matBadge` was placed on a `<mat-icon>` that has `aria-hidden="true"`. Because mat-icon hides itself from assistive technology by default, the badge content (like a notification count) would be invisible to screen readers. The badge directive warns in dev mode after view init and suggests explicitly exposing the icon.

Source

Thrown at src/material/badge/badge.ts:232

      this._badgeElement = this._createBadgeElement();
      this._updateRenderedContent(this.content);
    }

    this._isInitialized = true;
  }

  ngAfterViewInit() {
    if (typeof ngDevMode === 'undefined' || ngDevMode) {
      const nativeElement = this._elementRef.nativeElement;

      // Heads-up for developers to avoid putting matBadge on <mat-icon>
      // as it is aria-hidden by default docs mention this at:
      // https://material.angular.dev/components/badge/overview#accessibility
      if (
        nativeElement.tagName.toLowerCase() === 'mat-icon' &&
        nativeElement.getAttribute('aria-hidden') === 'true'
      ) {
        console.warn(
          `Detected a matBadge on an "aria-hidden" "<mat-icon>". ` +
            `Consider setting aria-hidden="false" in order to surface the information assistive technology.` +
            `\n${nativeElement.outerHTML}`,
        );
      }
    }
  }

  ngOnDestroy() {
    // ViewEngine only: when creating a badge through the Renderer, Angular remembers its index.
    // We have to destroy it ourselves, otherwise it'll be retained in memory.
    if (this._renderer.destroyNode) {
      this._renderer.destroyNode(this._badgeElement);
      this._inlineBadgeDescription?.remove();
    }

    this._ariaDescriber.removeDescription(this._elementRef.nativeElement, this.description);
  }

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Set `aria-hidden="false"` on the mat-icon so the badge is announced.
  2. Better: give the badge meaning via `matBadgeDescription`, e.g. `matBadgeDescription="3 unread messages"`.
  3. Or put the accessible label on the parent button and keep the icon hidden, ensuring the count is conveyed elsewhere.

Example fix

<!-- before -->
<mat-icon matBadge="5" aria-hidden="true">mail</mat-icon>

<!-- after -->
<mat-icon matBadge="5" matBadgeDescription="5 unread messages" aria-hidden="false">mail</mat-icon>
Defensive patterns

Strategy: validation

Validate before calling

const el = iconEl.nativeElement;
if (el.hasAttribute('matBadge') && el.getAttribute('aria-hidden') === 'true') {
  console.warn('Badge on aria-hidden icon; set aria-hidden="false" or add matBadgeDescription.');
}

Prevention

When it happens

Trigger: Template like `<mat-icon matBadge="3" aria-hidden="true">mail</mat-icon>` — the directive's `ngAfterViewInit` checks the host's tag name and `aria-hidden` attribute; also triggered when `aria-hidden="true"` is set via host binding on mat-icon.

Common situations: Notification badge on an icon button; developers add `aria-hidden="true"` to mat-icon to silence icon announcements without realizing the badge carries meaningful info.

Related errors


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