Lazy load fonts using the FontFace API and IntersectionObserver, without any dependencies.
Fonts are registered on demand via the FontFace API — no CSS @font-face declarations needed. Font URLs and metadata are provided via a JSON script element in the page.
- Eager elements load their font immediately on init
- Lazy elements load their font when scrolled into view via IntersectionObserver
- Classes are added after the font loads so CSS can react to them
For eager fonts, pair with <link rel="preload"> so the browser fetches the file before the script runs:
<link rel="preload" href="…/font.woff2" as="font" type="font/woff2" crossorigin><script src="font-loader.js" type="module"></script>Loads the font immediately. Use for fonts visible on page load.
<div data-font-load="eager" data-font-family="spacegrotesk-bold">
…
</div>Loads the font when the element scrolls into view (with a configurable root margin, default 300px).
<div data-font-load="lazy" data-font-family="spacegrotesk-bold">
…
</div>The class font-loaded is added to the element once the font is ready.
Font data is provided via a JSON script element. Each key corresponds to the value used in data-font-family.
<script id="font-metadata" type="application/json">
{
"spacegrotesk-bold": {
"name": "Space Grotesk Bold",
"family": "spacegroteskbold",
"weightValue": "700",
"weightName": "Bold",
"style": "normal",
"url": "https://…/space-grotesk-bold.woff2"
}
}
</script>| Field | Description |
|---|---|
name |
Human-readable font name |
family |
Font family name registered with the FontFace API |
weightValue |
Numeric weight (100–900) |
weightName |
Human-readable weight name |
style |
normal or italic |
url |
URL to the .woff2 file |
Override defaults via a global window.FontLoaderConfig object, set before loading the script:
<script>
window.FontLoaderConfig = {
rootMargin: '500px',
fontsLoadedClass: 'my-fonts-loaded'
};
</script>
<script src="font-loader.js" type="module"></script>| Key | Default | Description |
|---|---|---|
eagerSelector |
[data-font-load="eager"] |
Elements to load immediately |
lazySelector |
[data-font-load="lazy"] |
Elements to lazy load |
fontsLoadedClass |
fonts-loaded |
Class added when all fonts are loaded |
fontLoadedClass |
font-loaded |
Class added to a lazy element on load |
rootMargin |
300px |
IntersectionObserver root margin |
threshold |
0 |
IntersectionObserver threshold |
metadataSelector |
#font-metadata |
Selector for the JSON metadata script element |
font-loader exports loadFont and config for use by other scripts. The font-tester integration is handled separately in that repo and imports font-loader as a bare specifier via an import map.
Add the import map to your <head> before any <script type="module"> tags:
<script type="importmap">
{
"imports": {
"font-loader": "https://…/font-loader.js"
}
}
</script>Note: if your page uses a Content Security Policy with nonces, the import map tag also needs the nonce attribute.
A simple way to lazy load fonts. Intended for Fountain, a type foundry e-commerce platform. But it'll work anywhere.
MIT