components: Error when retrieving icon from registry in server side rendering

Bug, feature request, or proposal:

When we register some icons in MatIconRegistry and access to them in SSR, we have some errors and icons aren’t displayed.

What is the expected behavior?

Display icons and not have error in the console

What is the current behavior?

Icon aren’t displayed and we have error in the console

What are the steps to reproduce?

Create a component and register an icon in MatIconRegistry:

import {Component, OnInit} from '@angular/core';
import {MatIconRegistry} from '@angular/material';
import {DomSanitizer} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private _matIconRegistry: MatIconRegistry, private _domSanitizer: DomSanitizer) {
    this._matIconRegistry.addSvgIcon('loader', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/Icons/loader-default.svg'));
  }

  ngOnInit() {
  }

}
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    // Make AppModule compatible with Universal
    BrowserModule.withServerTransition({ appId: 'universal-app' }),
    MatIconModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Access to it in server side rendering:

<mat-icon svgIcon="loader"></mat-icon>

In the console you’ll have this message:

Error retrieving icon:
ERROR [Error]

What is the use-case or motivation for changing an existing behavior?

MatIconRegistry should work in SSR

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

"dependencies": {
    "@angular/animations": "^5.2.2",
    "@angular/cdk": "^5.1.1",
    "@angular/common": "^5.2.2",
    "@angular/compiler": "^5.2.2",
    "@angular/core": "^5.2.2",
    "@angular/forms": "^5.2.2",
    "@angular/http": "^5.2.2",
    "@angular/material": "^5.1.1",
    "@angular/platform-browser": "^5.2.2",
    "@angular/platform-browser-dynamic": "^5.2.2",
    "@angular/platform-server": "^5.2.2",
    "@angular/router": "^5.2.2",
    "@hapiness/config": "^1.1.1",
    "@hapiness/core": "^1.3.0",
    "@hapiness/ng-universal": "^5.2.2",
    "@hapiness/ng-universal-transfer-http": "^5.2.2",
    "@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5",
    "core-js": "^2.5.1",
    "rxjs": "^5.5.6",
    "zone.js": "^0.8.20"
  },
  "devDependencies": {
    "@angular/cli": "^1.6.6",
    "@angular/compiler-cli": "^5.2.2",
    "@angular/language-service": "^5.2.2",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "^9.4.0",
    "codelyzer": "^4.1.0",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-loader": "^3.3.1",
    "ts-node": "^4.0.1",
    "tslint": "^5.9.1",
    "typescript": "^2.6.2"
  }

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 23
  • Comments: 24 (1 by maintainers)

Most upvoted comments

For SSR, full URL is needed in order to retrieve svg icon. When encountered to this issue, I fixed it with something like this:

import {Component, OnInit, Inject} from '@angular/core';
import {MatIconRegistry} from '@angular/material';
import {DomSanitizer} from '@angular/platform-browser';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private _matIconRegistry: MatIconRegistry, private _domSanitizer: DomSanitizer, @Inject(PLATFORM_ID) private platformId: string) {
    const svgUrl = 'assets/Icons/loader-default.svg';
    // domain and port for SSR in this example is static. Use i.e. environment files to use appropriate dev/prod domain:port
    const domain = (isPlatformServer(platformId)) ? 'http://localhost:4000/' : ''; 
    this._matIconRegistry.addSvgIcon('loader', this._domSanitizer.bypassSecurityTrustResourceUrl(domain + svgUrl));
  }

  ngOnInit() {
  }

}

For SSR, full URL is needed in order to retrieve svg icon. When encountered to this issue, I fixed it with something like this:

import {Component, OnInit, Inject} from '@angular/core';
import {MatIconRegistry} from '@angular/material';
import {DomSanitizer} from '@angular/platform-browser';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private _matIconRegistry: MatIconRegistry, private _domSanitizer: DomSanitizer, @Inject(PLATFORM_ID) private platformId: string) {
    const svgUrl = 'assets/Icons/loader-default.svg';
    // domain and port for SSR in this example is static. Use i.e. environment files to use appropriate dev/prod domain:port
    const domain = (isPlatformServer(platformId)) ? 'http://localhost:4000/' : ''; 
    this._matIconRegistry.addSvgIcon('loader', this._domSanitizer.bypassSecurityTrustResourceUrl(domain + svgUrl));
  }

  ngOnInit() {
  }

}

Facing same issue, with version 10.0.2 of @nguniversal/express-engine. @milosbr solution works indeed, however reading angular’s doc about absolute urls, we shouldn’t have to do this, unless I’m missing something…

If you are using one of the @nguniversal/*-engine packages (such as @nguniversal/express-engine), this is taken care for you automatically. You don’t need to do anything to make relative URLs work on the server.

If you don’t need icons to be actually rendered by server you can just replace icons with epmty SVG

  private registerIcon(name: string, filename: string) {
    if (isPlatformServer(this.platformId)) {
      /* Register empty icons for server-side-rendering to prevent errors */
      this.matIconRegistry.addSvgIconLiteral(name, this.domSanitizer.bypassSecurityTrustHtml('<svg></svg>'));
    } else {
      this.matIconRegistry.addSvgIcon(name, this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/path/to/icons/${filename}.svg`));
    }
  }

In this case all icons will be empty in server response but frontend will load them after loading

I go this error instead error retrieving icon: <svg> tag not found and absolute path doesn’t solve this issue. I have searched through the internet and could barely find any solution or workaround for it, please help give me any hint if you can, thanks a lot

I ran into this issue as well and couldn’t solve my problem until I realized that I had an interceptor that was changing all outbound request headers to application/json. I fixed it by adding some extra logic to see if I was requesting a resource from the assets folder to fix it. If any of you are still having this issue you might look at that.

Just follow this receipt to use absolute paths on server side: https://angular.io/guide/universal#using-absolute-urls-for-server-requests

Even this example isn’t working.

https://stackblitz.com/angular/ygonjmkebrr

Seconding the concern something is up with mat-icons and svgs, even the angular material examples are broken

https://stackblitz.com/angular/yjprlknbxmgk?file=src%2Fapp%2Ficon-svg-example.html

Assets not properly configured in angular.json. Move assets directory to src then update angular.json and set "assets": ["src/assets"]

I’m also getting the error <svg> tag not found at MatIconRegistry... with just a regular non-SSR Angular project. Like @aparroccini said, even the example in the docs has the same error. Anyone know how to fix?

Make sure of the following:

  • Interceptor that modifies requests (make exception for .svg assets requests)
  • If you use angular-in-memory-web-api make sure to set passThruUnknownUrl: true
  • Assets files exist in dist after running the build in the same path you are trying to register them (you may need to change angular.json)

Got simular issue, but the icon appers suddenly after a few refreshes.