07 sie 2025
4 min

Lazy loading fontów w Angularze – jak przyspieszyć renderowanie treści?

Spotkałeś się z przypadkiem, gdzie po uruchomieniu aplikacji, przez pierwszą sekundę miałeś biały ekran? A może zauważyłeś, że po zniknięciu loadera nie pojawiał się od razu kontent strony?

Jeśli tak, to prawdopodobnie natknąłeś się na jeden z klasycznych problemów, w którym czcionki blokują renderowanie.

Najczęstszy sposób ładowania czcionek

W typowej aplikacji Angular czcionki czy ikony są ładowane statycznie, np. przez <link> w index.html

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

Przeglądarka czeka z renderowaniem tekstu, dopóki czcionka nie zostanie pobrana i zainicjalizowana. To zjawisko nazywamy FOIT (Flash of Invisible Text).

W praktyce oznacza to:

  • gorszy UX, ponieważ użytkownik widzi pusty ekran
  • słabsze wyniki w kontekście accessibility
  • niepotrzebne zużywanie zasobów do ładowania tych czcionek, które nie są nam w danym miejscu potrzebne

Na ogół ten sposób nie jest złym rozwiązaniem. Jednak w naszym przypadku musimy sięgnąć po inną praktykę – po lazy loading.

Lazy loading czcionek + font-display: swap

Zamiast dodawać <link> do czcionki od razu w pliku index.html (czyli jeszcze zanim Angular cokolwiek wyrenderuje), lepiej poczekać z jej załadowaniem do momentu, gdy aplikacja naprawdę zacznie wyświetlać konkretny widok.

Przykładowo:

  • Jeśli czcionka jest używana tylko w module dashboardu, możemy ją dodać dopiero podczas inicjalizacji komponentu dashboardowego
  • Lub w przypadku jednej czcionki w całej aplikacji — zaraz po uruchomieniu AppComponent w metodzie ngOnInit.

Finalnie link z czcionką będzie znajdował się w <head>, ale dodamy go dynamicznie.

Dzięki temu:

  • przeglądarka zaczyna ładować czcionkę później, kiedy jest naprawdę potrzebna,
  • zmniejszamy ilość zasobów pobieranych podczas pierwszego ładowania aplikacji,
  • ograniczamy blokowanie renderowania strony, ponieważ nie czekamy na pobranie czcionki zanim pokażemy użytkownikowi tekst

Mamy więc już rozwiązaną część problemu. Tylko część – ponieważ dalej nie mamy dostępnej czcionki na początku renderowania aplikacji.

I tutaj z pomocą przychodzi rozwiązanie font-display: swap. To parametr w CSS, który informuje przeglądarkę, jak zachować się w trakcie pobierania czcionki.

Standardowo przeglądarki wstrzymują wyświetlanie tekstu aż do momentu pobrania i załadowania czcionki, co prowadzi do efektu FOIT (Flash of Invisible Text) – czyli widzimy pustą przestrzeń tam, gdzie powinien być tekst.

Z parametrem font-display: swap przeglądarka:

  1. Od razu wyświetla tekst z czcionką systemową lub lokalną, którą ma już pod ręką,
  2. Równocześnie zaczyna ładować docelową czcionkę,
  3. Po zakończeniu ładowania podmienia czcionkę na właściwą – efekt jest płynny i praktycznie niezauważalny dla użytkownika.

Dlaczego to jest ważne?

  • Użytkownik już nie patrzy na pusty ekran, tylko widzi od razu czytelny tekst – nawet jeśli jeszcze nie mamy idealnej czcionki.
  • Dla SEO i Core Web Vitals (FCP, LCP) to ogromna poprawa, bo liczy się, kiedy pierwszy raz pojawi się widoczna i czytelna treść.

Przykładowa implementacja

W przykładzie poniżej możemy zobaczyć sposób ładowania docelowej czcionki z czcionki systemowej

Na końcu linku do czcionki możemy zauważyć parametr display=swap – który mówi Google Fonts, żeby do wygenerowanego CSS automatycznie dopisać font-display: swap w definicjach @font-face

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);
  }
}

Jeżeli jako tymczasową czcionkę chcemy ustawić tą zapisaną lokalnie – poza pobieraniem tej docelowej – musimy dodać konfigurację do pliku SCSS, np. do głównego 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;
}

Przy czym font-family: 'MyLocalFont’, system-ui, sans-serif; oznacza:

  1. Spróbuj wyświetlić tekst czcionką lokalną (MyLocalFont),
  2. Jeśli się nie uda, pokaż czcionkę systemową (system-ui),
  3. Jeśli jej nie ma, użyj ogólnej bezszeryfowej (sans-serif).

Przykładowe zestawienie Core Web Vitals

Metryka Przed lazy loading fontów Po zastosowaniu lazy loading fontów / font-display: swap Poprawa (%)
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%

Przy czym:

  • LCP (Largest Contentful Paint) – czas do załadowania największego elementu widocznego na ekranie. Optymalizacja fontów pozwala szybciej wyświetlić tekst, więc LCP spada.
  • CLS (Cumulative Layout Shift) – ilość nieoczekiwanych przesunięć layoutu. Lazy loading fontów i font-display: swap minimalizują zmiany wielkości tekstu po załadowaniu fontów.
  • FID (First Input Delay) – opóźnienie między pierwszą interakcją użytkownika a reakcją strony. Ogólnie może to nie mieć tak dużego wpływu, ale dzięki mniejszemu blokowaniu zasobów fontowych FID się poprawia.

Dane bazują na testach porównawczych stron internetowych przed i po wdrożeniu lazy loading fontów z użyciem font-display: swap i preload. Wyniki podobne do tych opisują m.in. Google Web.dev oraz HTTP Archive.

Podsumowanie

Zamiast pozwalać czcionkom blokować cały proces renderowania strony, możemy podejść do tego sprytniej. Lazy loading czcionek, połączony z font-display: swap, pozwala najpierw załadować czcionkę lokalną lub systemową, a dopiero potem wczytać tę docelową. Zyskujemy płynność, szybsze ładowanie widoku, lepsze wskaźniki Core Web Vitals i optymalizujemy wykorzystanie zasobów.

Takie podejście z lazy loadowaniem czcionek to prosta zmiana, a przynosi naprawdę odczuwalne efekty – i dla użytkownika, i dla wydajności aplikacji. 

Podziel się artykułem

Zapisz się na nasz newsletter

Dołącz do community Angular.love i bądź na bieżąco z trendami.