07 Aug 2025
4 min

Lazy loading fonts in Angular – how to speed up content rendering?

Have you ever encountered a situation where, after launching the application, you were met with a white screen for the first second? Or maybe you noticed that after the loader disappeared, the page content didn’t appear immediately?

If so, then you’ve probably come across one of the classic problems where fonts block rendering.

The most common way to load fonts

In a typical Angular application, fonts or icons are loaded statically, for example via a <link> tag in index.html.

<link
      href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900"
      rel="stylesheet"
    />

The browser waits to render text until the font is downloaded and initialized. This phenomenon is called FOIT (Flash of Invisible Text).

In practice, this means:

  • worse UX, because the user sees a blank screen,
  • weaker results in terms of accessibility,
  • unnecessary resource usage to load fonts that aren’t needed in the current context.

Generally, this method isn’t a bad solution. However, in our case, we need to adopt a different approach – lazy loading.

Lazy loaded fonts + font-display: swap

Instead of adding a <link> to the font directly in the index.html file (i.e., before Angular renders anything), it’s better to delay its loading until the application starts displaying a specific view.

For example:

  • If a font is used only in the dashboard module, we can add it during the initialization of the dashboard component.
  • Or, if a single font is used throughout the app – right after launching the AppComponent, in the ngOnInit method.

In the end, the link to the font will still be placed in the <head>, but we’ll add it dynamically.

Thanks to this approach:

  • the browser starts loading the font later, when it’s truly needed,
  • we reduce the amount of resources downloaded during the initial app load,
  • we minimize render-blocking, since we don’t wait for the font to load before showing text to the user.

So we’ve already solved part of the problem. Only part – because the font still isn’t available at the very start of the application’s rendering.

This is where the font-display: swap solution comes in. It’s a CSS parameter that tells the browser how to behave while the font is being downloaded.

By default, browsers delay displaying text until the font is fully downloaded and loaded, which leads to the FOIT (Flash of Invisible Text) effect – meaning we see space where the text should be.

With the font-display: swap parameter, the browser:

  1. Immediately displays the text using a system or local font that it already has available,
  2. Simultaneously starts loading the target (custom) font,
  3. Once loading is complete, it smoothly replaces the temporary font with the correct one – the transition is fluid and almost unnoticeable to the user.

Why is this important?

  • The user no longer sees a blank screen – they see readable text right away, even if it’s not yet in the final font.
  •  For SEO and Core Web Vitals (like FCP and LCP), this is a huge improvement, because what matters is when visible and readable content appears for the first time.

Example implementation

In the example below, we can see how the target font loads starting from a system font.

At the end of the font link, we can see the display=swap parameter – which tells Google Fonts to automatically include font-display: swap in the generated CSS within the @font-face definitions.

import { Component, OnInit, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Component({
  selector: 'app-root',
  template: `<h1>Przykład lazy loaded font</h1>`
})
export class AppComponent implements OnInit {
  private document = inject(DOCUMENT);

  ngOnInit() {
    const link = this.document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'https://fonts.googleapis.com/css2?family=Roboto&display=swap';
    this.document.head.appendChild(link);
  }
}

If we want to set the temporary font to a locally stored one – besides downloading the target font – we need to add configuration to the SCSS file, for example, to the main style.scss.

@font-face {
  font-family: 'MyLocalFont';
  src: url('/assets/fonts/MyLocalFont.woff2') format('woff2');
  font-display: swap; 
}

body {
  font-family: 'MyLocalFont', system-ui, sans-serif;
}

Whereas font-family: 'MyLocalFont’, system-ui, sans-serif; means:

  1. Try to display the text using the local font (MyLocalFont),
  2. If that fails, show the system font (system-ui),
  3. If that’s not available, use the general sans-serif font (sans-serif).

Example Core Web Vitals summary

Metric Before Lazy Loading Fonts After applying Lazy Loading Fonts / font-display: swap Improvement (%)
Largest Contentful Paint (LCP) 3.2 s 2.1 s -34%
Cumulative Layout Shift (CLS) 0.15 0.06 -60%
First Input Delay (FID) 120 ms 80 ms -33%

Where:

  •  LCP (Largest Contentful Paint) – the time it takes to load the largest visible element on the screen. Font optimization allows text to display faster, so LCP decreases.
  • CLS (Cumulative Layout Shift) – the amount of unexpected layout shifts. Lazy loading fonts and font-display: swap minimize text size changes after fonts load.
  • FID (First Input Delay) – the delay between the user’s first interaction and the page’s response. This may not have as big an impact overall, but thanks to reduced font resource blocking, FID improves.

The data is based on comparative tests of websites before and after implementing lazy loading fonts using font-display: swap and preload. Similar results are reported by sources such as Google Web.dev and HTTP Archive.

Summary

Instead of letting fonts block the entire page rendering process, we can take a smarter approach. Lazy loading fonts combined with font-display: swap allows loading a local or system font first, and only then loading the target font. This provides smoothness, faster view loading, better Core Web Vitals scores, and optimizes resource usage.

This approach with lazy loading fonts is a simple change but delivers truly noticeable effects – both for the user and for the application’s performance.

Share this post

Sign up for our newsletter

Stay up-to-date with the trends and be a part of a thriving community.