ABHILASH SB
ABHILASH SB

Reputation: 2202

How can we preload fonts in Angular?

Should we include the fonts in index.html file with rel="preload" like the below code or can we configure this in Angular CLI to preload all the fonts required?
Please suggest me a better solution as I can see it takes multi-second page load time suggested in Google Analysis.

<link rel="preload" href="./assets/fonts/Lato/Lato-Semibold.woff2" as="font" crossorigin>
<link rel="preload" href="./assets/fonts/Lato/Lato-Black.woff2" as="font" crossorigin>
<link rel="preload" href="./assets/fonts/Lato/Lato-Bold.woff2" as="font" crossorigin>
<link rel="preload" href="./assets/fonts/Lato/Lato-Heavy.woff2" as="font" crossorigin>
<link rel="preload" href="./assets/fonts/Lato/Lato-Medium.woff2" as="font" crossorigin>
<link rel="preload" href="./assets/fonts/Lato/Lato-Regular.woff2" as="font" crossorigin>

Upvotes: 20

Views: 8192

Answers (6)

Ajay Choudhary
Ajay Choudhary

Reputation: 281

The best option you can try by using APP_INITIALIZER like this:

Create a FontProvider:

export const FontProvider = () => {
    const fonts = [
        {
            family: "Lato Regular",
            src: "./assets/fonts/Lato/Lato-Regular.woff2",
            options: {
                weight: '400',
                style: 'normal'
            }
        },
        {
            family: "Lato Semibold",
            src: "./assets/fonts/Lato/Lato-Semibold.woff2",
            options: {
                weight: '600',
                style: 'normal'
            }
        }
    ];

    for (const {family, src, options} of fonts) {
        const font = new FontFace(family, src, options);
        font.load()
            .then(() => document.fonts.add(font))
            .catch(err => console.log(err))
    }
}

Now, add this FontProvider to you app.module.ts file :

import { APP_INITIALIZER, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppComponent } from './app.component';
import { BrowserModule } from '@angular/platform-browser';
import { FontProvider } from './font.provider.ts'
@NgModule({
  imports: [CommonModule, BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: FontProvider,
      multi: true
    }
  ]
})
export class AppModule {}

This will work 100% and let me know if you need some other suggestion or help.

Upvotes: 1

Pierre
Pierre

Reputation: 9052

Only way I could come up with to force the browser to pre-download the font files, is to actually use the font.

Create a font-preload.css file ie. /assets/fonts/Lato/font-preload.css:

@font-face {
    font-family: 'Lato';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url(Lato-Regular.woff2) format('woff2');
}
@font-face {
    font-family: 'Lato';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url(Lato-Medium.woff2) format('woff2');
}
@font-face {
    font-family: 'Lato';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url(Lato-Semibold.woff2) format('woff2');
}
@font-face {
    font-family: 'Lato';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url(Lato-Bold.woff2) format('woff2');
}
@font-face {
    font-family: 'Lato';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url(Lato-Heavy.woff2) format('woff2');
}
@font-face {
    font-family: 'Lato';
    font-style: normal;
    font-weight: 900;
    font-display: swap;
    src: url(Lato-Black.woff2) format('woff2');
}

/* font pre-load class with off-screen invisible text */
.f-pl {
    position: absolute;
    top: -14000px;
    left: -14000px;
    font-size: 0.001px;
    width: 0;
    height: 0;
    color: transparent
}

/* For each font, create a class with the font-family and its weight
   which matches the font file to be downloaded */
.f-pl.lato-regular {
    font-family: 'Lato';
    font-weight: 400;
}
.f-pl.lato-medium {
    font-family: 'Lato';
    font-weight: 500;
}
.f-pl.lato-semibold {
    font-family: 'Lato';
    font-weight: 600;
}
.f-pl.lato-bold {
    font-family: 'Lato';
    font-weight: 700;
}
.f-pl.lato-heavy {
    font-family: 'Lato';
    font-weight: 800;
}
.f-pl.lato-black {
    font-family: 'Lato';
    font-weight: 900;
}

Include the css stylesheet in the end of the <head> tag and also elements with the f-pl class for each font in the <body> tag:

    <link href="assets/fonts/Lato/font-preload.css" rel="stylesheet">
</head>
<body>
    <!-- use the fonts so the browser downloads the font files -->
    <a class="f-pl lato-regular">1</a>
    <a class="f-pl lato-medium">1</a>
    <a class="f-pl lato-semibold">1</a>
    <a class="f-pl lato-bold">1</a>
    <a class="f-pl lato-heavy">1</a>
    <a class="f-pl lato-black">1</a>

    <app-root></app-root>

The .f-pl class will move the tiny transparent text off-screen and out of view with its position: absolute; and top: -14000px; left: -14000px;.

If you have fonts with unicode-range: for specific unicode ranges, just create a class for that font + weight then instead of 1 as the character, use a character in that specific unicode range.

This also works for Material Icons/Symbols for instance:

.f-pl.material-symbols-outlined {
    font-family: 'Material Symbols Outlined'
}
.f-pl.material-symbols-sharp {
    font-family: 'Material Symbols Sharp'
}
.f-pl.material-symbols-rounded {
    font-family: 'Material Symbols Rounded'
}

Then in the <body> tag:

<a class="f-pl material-symbols-outlined">1</a>
<a class="f-pl material-symbols-sharp">1</a>
<a class="f-pl material-symbols-rounded">1</a>

Upvotes: 0

Somil Dubey
Somil Dubey

Reputation: 1

Including multiple preload links for each font file can bloat your html file and make it harder to manage.

1. You can create a service in angular to handle font preloading. This will allow flexibility.

2. You can also configure your angular.json file

"assets": [
    "src/favicon.ico",
    "src/assets",
    {
      "glob": "**/*",
      "input": "node_modules/@fortawesome/fontawesome-free/webfonts",
      "output": "/webfonts/"
    }
]

3. If the fonts you have are not part of application standard assets, you can use a service as

In service.ts


import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class FontPreloadingService {
  preloadFonts(fontUrls: string[]) {
    fontUrls.forEach((url) => {
      const link = document.createElement('link');
      link.rel = 'preload';
      link.as = 'font';
      link.href = url;
      link.crossOrigin = 'anonymous';
      document.head.appendChild(link);
    });
  }
}

Preload the fonts in your component 

In app.ts

import { Component, OnInit } from '@angular/core';
import { FontPreloadingService } from './font-preloading.service';

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

  ngOnInit() {
    const fontUrls = [
      './assets/fonts/Lato/Lato-Semibold.woff2',
      './assets/fonts/Lato/Lato-Black.woff2',
      // Add other font URLs as needed
    ];

    this.fontPreloadingService.preloadFonts(fontUrls);
  }
}

Upvotes: 0

Mahima Sachdeva
Mahima Sachdeva

Reputation: 1

For this implementation in angular, you should use third-party dependency to load the fonts.,Find centralized, trusted content and collaborate around the technologies you use most.,Connect and share knowledge within a single location that is structured and easy to search. After that load your custom font like this

   WebFont.load({
              custom: {
                 families: [""
                    Lato "]
                 }
              });

Upvotes: -2

Pushpinder Singh
Pushpinder Singh

Reputation: 71

  1. Preconnect to the font file origin.
  2. Preload the font stylesheet asynchronously with low priority.
  3. Asynchronously load the font stylesheet and font file after the content has been rendered with JavaScript.
  4. Provide a fallback font for users with JavaScript turned off.

Example::

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2family=Merriweather&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2family=Merriweather&display=swap" media="print" onload="this.media='all'" />
<noscript>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2family=Merriweather&display=swap" />
</noscript>

For your reference : Link to Harry Roberts explanation on fonts loading

Upvotes: 0

Raheem Mohamed
Raheem Mohamed

Reputation: 797

For this implementation in angular, you should use third-party dependency to load the fonts.

use webfontloader

npm i webfontloader

After that load your custom font like this

 WebFont.load({
  custom: {
    families: [""Lato"]
  }
 });

Upvotes: -2

Related Questions