loading svg icons in Angular

Loading SVG icons in Angular

In modern web development, icons play a crucial role in creating intuitive and visually appealing user interfaces. Scalable Vector Graphics (SVG) have become the preferred format for icons due to their scalability, flexibility, and small file size. If you’re working with Angular, you’ll be pleased to know that it provides various methods for efficiently loading SVG icons into your application. In this blog post, we will explore different approaches to loading SVG icons in Angular, enabling you to streamline your icon management process.

Using SVG as an inline resource:

One of the simplest ways to load SVG icons in Angular is by treating them as inline resources. This approach involves directly embedding the SVG markup within your component templates. You can copy the SVG code from an external source or create your own SVG markup. Angular’s template syntax allows you to dynamically bind attributes and manipulate the SVG as needed. This method is straightforward, but it isn’t a robust solution as it adds lots of markup code to your templates, and if you want to reuse some icons you need to either copy icon code or wrap it in custom component.

Using the Angular Material Icon library:

Angular Material, a UI component library for Angular, includes a comprehensive set of pre-built SVG icons. By utilizing the Angular Material Icon library, you can easily incorporate these icons into your application. Angular Material provides a mat-icon component that allows you to specify the icon name and color, simplifying the process of adding icons to your templates. In addition to the default icons, you can also register your own icons. Angular Material icons support theming and customization options, making it a powerful choice for managing icons in Angular applications. In my opinion, this solution has only one downside – you have to install the Angular Material library which usually isn’t a problem as this is a very powerful and easy-to-use library, but if for some reason you can’t use this library then you can use next proposed solution:

Leveraging third-party icon libraries:

Another approach to loading SVG icons in Angular is to take advantage of third-party icon libraries. Libraries like Font Awesome, Material Design Icons, or Feather Icons provide extensive collections of SVG icons that can be easily integrated into Angular applications. These libraries typically offer Angular-specific packages or directives that simplify the process of using their icons. By installing the required packages and following the provided documentation, you can quickly incorporate these icons into your Angular components.

Creating a custom SVG icon component:

For more advanced scenarios or when you want to have complete control over your icon management, creating a custom SVG icon component can be a viable option. This approach involves creating a reusable Angular component that accepts parameters such as icon name, size, color, and additional customization options. Inside the component, you can use Angular’s Renderer2 or ElementRef to dynamically load the SVG file and apply the specified properties. This method provides flexibility and allows you to encapsulate complex icon loading logic, making it easier to maintain and reuse icons across your application.

Here is an example implementation of such component:

import { Component, Input, ElementRef, Renderer2, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError, tap } from 'rxjs/operators';
import { of } from 'rxjs';

@Component({
    selector: 'app-svg-icon',
    template: '<ng-content></ng-content>'
})
export class SvgIconComponent implements OnInit {
    @Input() iconName: string;
    @Input() iconSize = '24px';
    @Input() iconColor = 'black';

    constructor(private http: HttpClient, private elementRef: ElementRef, private renderer: Renderer2) {}

    ngOnInit(): void {
        this.loadIcon();
    }

    private loadIcon(): void {
        const iconPath = `assets/icons/${this.iconName}.svg`;

        this.http
            .get(iconPath, { responseType: 'text' })
            .pipe(
                tap((svgText) => {
                    const svgElement = this.renderer.createElement('span');
                    svgElement.innerHTML = svgText;
                    const icon = svgElement.querySelector('svg');
                    this.applyIconStyles(icon);
                    this.elementRef.nativeElement.appendChild(icon);
                }),
                catchError((error: any) => {
                    console.error(`Failed to load SVG icon '${this.iconName}':`, error);
                    return of(null);
                })
            )
            .subscribe();
    }

    private applyIconStyles(svgElement: HTMLElement): void {
        this.renderer.setStyle(svgElement, 'width', this.iconSize);
        this.renderer.setStyle(svgElement, 'height', this.iconSize);
        this.renderer.setStyle(svgElement, 'fill', this.iconColor);
    }
}

Here is example usage of this component:

<app-svg-icon iconName="my-icon" iconSize="32px" iconColor="blue"></app-svg-icon>

Make sure to place your SVG icons in the assets/icons folder relative to your Angular project root directory. The file name should match the iconName input value.

This implementation dynamically loads the SVG file using the HttpClient API and creates an SVG element dynamically using Angular’s Renderer2. It applies the specified icon size and color using the applyIconStyles method.

This approach can be improved by adding a service that will prevent loading the same icons multiple times, by memoizing them.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
import { firstValueFrom, of } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class IconService {
    private cache: { [key: string]: SVGElement } = {};

    constructor(private http: HttpClient) {}

    getIcon(iconName: string): Promise<SVGElement> {
        if (this.cache[iconName]) {
            return Promise.resolve(this.cache[iconName].cloneNode(true) as SVGElement);
        }

        const iconPath = `assets/icons/${iconName}.svg`;

        return firstValueFrom( // in rxjs 6 you can use toPromise() instead
            this.http.get(iconPath, { responseType: 'text' }).pipe(
                map((svgText) => {
                    const svgElement = document.createElement('div');
                    svgElement.innerHTML = svgText;
                    const icon = svgElement.querySelector('svg');
                    this.cache[iconName] = icon;
                    return icon.cloneNode(true) as SVGElement;
                }),
                catchError((error) => {
                    console.error(`Failed to load SVG icon '${iconName}':`, error);
                    return of(null);
                })
            )
        );
    }
}

With such service update the loadIcon method in a SvgIconComponent

private loadIcon(): void {
    this.iconsService.getIcon(this.iconName).then((svgElement: SVGElement) => {
        if (svgElement) {
            this.applyIconStyles(svgElement);
            this.elementRef.nativeElement.appendChild(svgElement);
        }
    });
}

With the updated SvgIconComponent and the new IconService, the service memoizes loaded icons using a cache object (this.cache). When getIcon is called for an icon that has already been loaded, it returns a clone of the cached SVG element to prevent multiple requests and improve performance.

The IconService handles fetching and parsing the SVG file, storing the parsed element in the cache for future use. If an error occurs during loading, it logs the error and returns null.

By utilizing the IconService in the SvgIconComponent, the component now benefits from the memoization feature when loading icons

Feel free to customize this implementation further to fit your specific needs, such as adding error handling or additional customization options.

Conclusion

Icons play a vital role in enhancing the user experience and visual appeal of web applications. In Angular, loading SVG icons can be achieved using different approaches, each with its own advantages. Whether you choose to embed SVG code directly, leverage Angular Material, utilize third-party icon libraries, or create custom icon components, Angular provides flexibility and extensibility to meet your icon management needs. By adopting efficient techniques for loading SVG icons in Angular, you can streamline your development process, improve code maintainability, and deliver exceptional user interfaces that are both intuitive and visually stunning.

1 thought on “Loading SVG icons in Angular”

  1. Pingback: Angular Load Time Performance Optimization: Accelerating Your Application - Quality Coders

Leave a Comment

Your email address will not be published. Required fields are marked *