htmlcssfontsfont-face

Font preloading isn't working in most major browsers (except it is working in macOS Safari)


I'm trying to preload the font before the page is rendered

I've already searched for solutions and found the following answers on StackOverflow:

I tried them but they aren't working

<link rel="preload" href="/fonts/FuzzyBubbles-Regular.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/FuzzyBubbles-Bold.woff2" as="font" type="font/woff2" crossorigin>

I even tried to:

But none of these resolved the issue


Demo

To make the issue easier to reproduce I delayed the font file response in Expressjs by 3secs (this is just for testing-purposes to simulate slow internet condition) => You'll notice that:

This is the index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Font Preloading Demo</title>

    <link rel="preload" href="/fonts/FuzzyBubbles-Regular.woff2" as="font" type="font/woff2" crossorigin>
    <link rel="preload" href="/fonts/FuzzyBubbles-Bold.woff2" as="font" type="font/woff2" crossorigin>

    <style>
        @font-face {
            font-family: 'FuzzyBubbles';
            src: url('/fonts/FuzzyBubbles-Regular.woff2') format('woff2');
            font-weight: normal;
            font-style: normal;
            font-display: swap;
        }
        @font-face {
            font-family: 'FuzzyBubbles';
            src: url('/fonts/FuzzyBubbles-Bold.woff2') format('woff2');
            font-weight: 700;
            font-style: normal;
            font-display: swap;
        }

        body {
            font-family: "FuzzyBubbles", sans-serif;
        }
    </style>
</head>
<body>
    <h1>Font Preloading Demo</h1>
    <p>Here Some Text Goes... Here Some Text Goes...</p>
</body>
</html>

And there's the index.js (the server code that delays the font file response by 3secs)

const express = require('express');
const path = require('path');

const app = express();
const PORT = 8080;

app.use((req, res, next) => {
    if (req.url.includes('fonts')) {
        setTimeout(next, 3000);
    } else {
        next();
    }
});

app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.listen(PORT, () => {
    console.log(`http://localhost:${PORT}`);
});


Solution

  • Webkit/Safari's behavior is weird – preload is not intended for render blocking

    While Webkit's behavior may suit your specific needs best – a <link rel="preload"> is not supposed to block the entire rendering.

    Most engines Chromium/Blink (google chrome, Brave, Vivaldi, Opera, Edge etc) based browsers as well as Firefox/Gecko aim for a reasonable balance between waiting for assets such as fonts or stylesheets and a first meaningful rendering.

    If the internet connection is too slow, at least render something meaningful, even if this means accepting layout shifts or FOUCs (flash of unstyled content).

    Concept of preload

    From mdn docs preload:

    Even though the name contains the term load, it doesn't load and execute the script but only schedules it to be downloaded and cached with a higher priority.

    Ensure all fonts are loaded

    In case you need to be 100% percent sure, all fonts are loaded before rendering content you need to wrap your rendering in a async function loading the fonts via fontface API.

    This is a common issue for <canvas> text renderings. See also "How can I use custom fonts in an HTML5 Canvas element?"

    Optimize font loading times

    While preload can optimize loading times you should first try to load the most compact font files – so better prefer woff2 over truetype.
    Woff2 files are significantly more compact. Also the truetype versions provided by google are not subset so they include the complete unicode range.

    Subsets

    Using subsets can significantly reduce the loading times as the browser will only load fonts for the required text. For instance, if you don't have any vietnamese text eg only English text the browser loads only the latin subset.

    font-display

    You may also increase the allowed render-blocking time via font-display: block.

    You can use a service like google webfont helper to get woff2 files for local hosting.

    All in all you can't reliably ensure all fonts are loaded before rendering with preload or font-display.