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:
crossorigin
to crossorigin="anonymous"
@font-face
directly in <style>
tag (instead of defining them in an external stylesheet)<link rel="preload" ... />
before/after the <style>
tag (or the external stylesheet)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}`);
});
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).
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.
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?"
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.
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.
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.