DEV Community

khalil la
khalil la

Posted on • Edited on

The Ultimate Guide to SVG Icons in Angular: From Basic to Semantic

Working with SVG icons in Angular projects can be approached in several ways, each with distinct trade-offs. Let's explore the evolution from basic implementations to an elegant, semantic solution.

The img Tag Approach

The simplest method uses a standard img tag:

<img src="assets/icons/my-icon.svg" alt="My Icon">
Enter fullscreen mode Exit fullscreen mode

This approach offers automatic browser caching, but transforms your SVG into a raster image. You lose the ability to style the icon dynamically—changing colors, applying CSS transforms, or responding to hover states becomes impossible.

Using mat-icon and Similar Libraries

Angular Material's mat-icon provides a more integrated solution:

<mat-icon svgIcon="my-icon"></mat-icon>
Enter fullscreen mode Exit fullscreen mode

While functional, this approach introduces complexity. Each icon requires an HTTP request to load and inline the SVG content. Additionally, you're wrapping your SVG with extra DOM elements, creating unnecessary nesting that can complicate styling and impact performance when using many icons.

SVG Sprites: The Traditional Performance Solution

SVG sprites bundle multiple icons into a single file, reducing HTTP requests and improving performance:

<!-- sprite.svg -->
<svg xmlns="http://www.w3.org/2000/svg">
  <defs>
    <symbol id="star" viewBox="0 0 24 24">
      <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
    </symbol>
    <symbol id="heart" viewBox="0 0 24 24">
      <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
    </symbol>
  </defs>
</svg>
Enter fullscreen mode Exit fullscreen mode

Usage in templates:

<svg class="icon">
  <use href="assets/sprite.svg#star"></use>
</svg>
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • Single HTTP request for all icons
  • Good browser caching
  • Maintains SVG styling capabilities

Disadvantages:

  • Manual sprite generation and maintenance
  • All icons loaded regardless of usage (no tree-shaking)
  • Icon identification still relies on string IDs

Direct Template Embedding

The third approach involves copying SVG code directly into your templates:

<svg viewBox="0 0 24 24" fill="currentColor">
  <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
</svg>
Enter fullscreen mode Exit fullscreen mode

This method works excellently for styling and performance, but creates two significant challenges:

Icon Identification Issues

When reviewing code, it's impossible to determine which icon an SVG represents without visual inspection or reverse-engineering the path data.

Code Duplication

Reusing the same icon across multiple components leads to duplicated SVG markup, violating the DRY principle and making updates cumbersome.

Component-Based Solution: Close, But Not Perfect

Creating dedicated components for each icon solves the identification and duplication problems:

@Component({
  selector: 'app-star-icon',
  template: `
    <svg viewBox="0 0 24 24" fill="currentColor">
      <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
    </svg>
  `
})
export class StarIcon {}
Enter fullscreen mode Exit fullscreen mode

This approach provides semantic naming and leverages Angular's tree-shaking to exclude unused icons from your bundle. However, it introduces a new problem: the host element.

The Host Element Challenge

Component-based icons create an additional DOM layer:

<!-- What you write -->
<app-star-icon class="text-blue-500 size-6"></app-star-icon>

<!-- What gets rendered -->
<app-star-icon class="text-blue-500 size-6">
  <svg viewBox="0 0 24 24">...</svg>
</app-star-icon>
Enter fullscreen mode Exit fullscreen mode

This host element complicates CSS styling. Direct child selectors don't work as expected, and you often need to use CSS tricks to bypass the wrapper element.

The Elegant Solution: Attribute Selectors

Angular's attribute selector feature provides the perfect solution. Instead of using element selectors, we can create components with attribute selectors:

@Component({
  selector: 'svg[si-star-icon]',
  template: `
    <svg:path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
  `,
  host:{
    '[attr.viewBox]': 'viewBox()',
  }
})
export class StarIcon {
  readonly viewBox = input<string>('0 0 24 24');
}
Enter fullscreen mode Exit fullscreen mode

Now you can use icons like this:

<svg class="text-blue-500 size-6" si-star-icon></svg>
Enter fullscreen mode Exit fullscreen mode

This approach combines the best of all worlds:

  • Semantic naming: Clear icon identification
  • No host element: Direct SVG styling
  • No duplication: Reusable components
  • Tree-shaking: Unused icons are excluded
  • Performance: No HTTP requests

The Time Investment Challenge

Creating attribute selector components for every SVG icon requires significant setup time and maintenance effort. You need to convert each icon, create components, and manage the collection.

Introducing @semantic-icons

To solve the time investment problem, I've created @semantic-icons—a comprehensive collection of popular icon libraries pre-converted for Angular attribute selector usage.

The project includes icons from:

  • Lucide Icons
  • Heroicons
  • Tabler Icons
  • And many more

Usage Example

Want to use the bird icon from Lucide? Simply:

<svg class="text-blue-500 size-6" si-bird-icon></svg>
Enter fullscreen mode Exit fullscreen mode

The naming convention follows a simple pattern: si-{icon-name}-icon

Getting Started

Visit the project repository: https://github.com/khalilou88/semantic-icons

The library provides thousands of ready-to-use SVG icons that integrate seamlessly with your Angular projects, requiring zero configuration and offering maximum styling flexibility.

Conclusion

SVG icon implementation in Angular has evolved from simple image tags to sophisticated, semantic solutions. The attribute selector approach with @semantic-icons represents the current best practice, offering developers a powerful, efficient, and maintainable way to work with SVG icons.

This solution eliminates the common pain points of icon management while preserving all the benefits of inline SVG usage. Give it a try in your next Angular project—your future self will thank you.

Top comments (0)