Include used material icons only

This article demonstrates a way to include just the material-design icons used. Since the material iconfont is about 44kB it is way too much to import the complete icon font for just a few icons. The other advantage is the control over the loading procedure. One is able to preload (or lazy load) each icon, or icon bunches, individually with the usage of service workers.
At the beginning your folder structure should be similar to the following example. In there all the regarding files are included, dropped all non-important files for this task.
At first we will have a look at the icons.module.ts. In here we import the HttpClientModule and add a forRoot() method to provide the service in the root component.
import { NgModule, ModuleWithProviders } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { IconService } from './services/icon'; @NgModule({ imports: [ HttpClientModule ] }) export class IconsModule { static forRoot(keys: string[], path: string): ModuleWithProviders { return { ngModule: IconsModule, providers: [ IconService, { provide: 'icons', useValue: { keys, path } } ] }; } }
Listing: icons.module.ts
In the icon.ts (service) file inject the MatIconRegistry, the DomSanitizer and icons. For each icon we passed in in the application module the MatIconRegistry adds a keypair with an icon key (e.g. like account_circle or other mat icons) and a safeUrl.
import { Injectable, Inject } from '@angular/core'; import { MatIconRegistry } from '@angular/material'; import { DomSanitizer } from '@angular/platform-browser'; interface IconsConfig { keys: string[]; path: string; } @Injectable() export class IconService { icons: IconsConfig; constructor ( private iconRegistry: MatIconRegistry, private domSanitizer: DomSanitizer, @Inject('icons') icons: IconsConfig ) { this.icons = icons; } register() { for (const icon of this.icons.keys) { this.iconRegistry.addSvgIcon( icon, this.domSanitizer.bypassSecurityTrustResourceUrl(`${this.icons.path}/baseline-${icon}-24px.svg`) ); } } }
Listing: Icon service
Now its allmost done. Just import the IconsModule in the AppModule with the usage of the forRoot() function and pass in the icons and the assets folders path.
import { IconsModule } from './modules/mat/icons.module'; import { icons } from './config/icons'; @NgModule({ imports: [ IconsModule.forRoot(icons, './assets/mat-icons') ] }) export class AppModule {}
Listing: Import the IconsModule
The icons variable is simply an array of strings that should match the pattern in between the filenames, e.g. baseline-account_circle-24px.svg. The icon config file app/config/icons.ts could look like
export const icons = [ 'account_circle', 'add', ];
Listing: Define icons
Then register the material icons where you need them, e.g. in the AppComponent.
@Component({ ... }) export class AppComponent { constructor (private iconService: IconService) { this.iconService.register(); } }
Listing: Register the icons
Afterwards you can use the generated material icons using
<mat-icon svgIcon="account_circle"></mat-icon>
Listing: Usage in templates
In this page I included, at the stage of writing this article, 19 SVG material icons which a size range from 100 to 400 bytes. This results in a final output size s of
1.9kB<s<7.6kB1.9\text{kB} < s < 7.6\text{kB}
The actual size is 4.4kB. The .woff2 material font is 44kB heavy. The library weights 1.6kB. The savings d are
d=44kB4.4kB1.2kB=38.4kBd= 44\text{kB} - 4.4\text{kB} - 1.2\text{kB} = 38.4\text{kB}

Conclusion

I think everybody has to dicide if 36kB are worth the effort for its own. Since I was interested for this website to include just necessary icons, I came up with this solution I wanted to share with you.

Comments

Felix Lemke on 9/29/2018, 12:03:24 PM
I've added a mat-icon-import library to npm.

Any questions? Want to join the discussion?

Sign in or sign up