Embed SVG code directly in HTML in Angular

In an Angular application, I wanted to render an SVG with custom CSS applied to specific elements. I didn't wanted to use an <img> tag since it doesn't allow for styling individual SVG elements. However, embedding raw SVG code directly in the component's HTML felt inefficient, as updating the SVG would require manual changes.

That's why I developed an Angular component that dynamically loads the SVG from its file path and embeds it in the DOM, which allowed me to style its elements with (S)CSS.

In this post I want to share with you how you could create a component like that.

Creating the SVG Loader

Create a standalone component, I name it 'SvgLoader'. This component contains a path input property, which will contain the path of an SVG file.

Inside the component, we use Angular's HttpClient to load the data from the SVG file. After receiving the data, we need to bypass sanitization by using the DomSanitizer, so we could inject the SVG tags directly into the HTML.

TypeScript code of the component:

import { HttpClient } from '@angular/common/http';
import { Component, inject, input, OnInit, signal } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Component({
  selector: 'app-svg-loader',
  templateUrl: './svg-loader.component.html',
  styleUrl: './svg-loader.component.scss'
})
export class SvgLoaderComponent {

  // This input-property will contain the file path of the SVG file.
  readonly path = input<string>('');

  // This will hold the file content (the SVG tag and its elements).
  readonly fileContent = signal<SafeHtml>(null);

  // Services we need to inject.
  private domSanitizer = inject(DomSanitizer);
  private http = inject(HttpClient);

  constructor() {
    effect(() => {
      // When the 'path' input value changes, we load the file data.
      const filePath = this.path();
      if (!filePath) {
        this.fileContent.set('');
        return;
      }
      this.loadFileData(filePath);
    });
  }
  
  private loadFileData(filePath: string): void {
    this.http
      .get(filePath, { responseType: 'text' })
      .subscribe({
        next: (data) => {
          if (data) {
            // When the file data has been loaded, we need to disable
            // sanitization, so we could load it directly into the HTML.
            const content = this.domSanitizer.bypassSecurityTrustHtml(data);
            this.fileContent.set(content);
          }
        }
      });
  }
  
}

HTML template of the component:

@if (fileContent(); as fileContent) {
  <div class="svg-loader" [innerHTML]="fileContent"></div>
}

Using the SVG Loader

Now we have the SVG Loader, let's use it inside another component.

First we need to include the SVG Loader component into the HTML template by adding its app-svg-loader tag. We set the path property to the file path of the SVG image we want to render.

<div class="page-component">
  <p>Sometimes you want show something.</p>
  <app-svg-loader path="/path/to/cool-image.svg" />
</div>

Then we add this component to the import array of the component.

To be able to style SVG elements from this parent component, we need to set the ViewEncapsulation of the component to None. This allows us to style the SVG tags, otherwise we could not apply styling to child components.

Another way to style the SVG elements is to add the (S)CSS style rules to the global stylesheet(s) instead of adding it to the styles of this parent component.

@Component({
  selector: 'app-parent-page',
  templateUrl: './parent-page.component.html',
  styleUrl: './parent-page.component.scss',
  imports: [
    SvgLoaderComponent // <-- Added the SVG Loader component.
  ],
  encapsulation: ViewEncapsulation.None // <-- Set ViewEncapsulation to None.
})
export class ParentPageComponent {
  // ...
}

Important!

When you implement this logic for your own component, it's important to know that the SVG file is not sanitized. This means that the file content is not checked for possible dangerous code and Angular has not removed it! This is because we bypass Angular's sanitization step which prevent Cross Site Scripting Security bugs (XSS).

Don't use this component, or its logic, for a (SVG) file that could by uploaded or modified by a user (a possible 'attacker')!

References

Angular's documentation about DomSanitizer