㊗️ Universal Webfont loader - Unfonts
This plugin goes beyond just generating font-face rules - it also takes care of link preload and prefetch, optimizing font loading for a faster and more efficient user experience.
Unfonts currently supports popular font providers like Typekit, Google Fonts, and Fontsource, as well as custom fonts. This gives you the flexibility to choose from a vast range of fonts and seamlessly integrate them into your projects.
With Unfonts, you no longer have to manually manage font files and font-face rules, or worry about slow loading times. Our package does all the heavy lifting for you, so you can focus on creating amazing content with ease.
View configuration:
npm i --save-dev unplugin-fonts # pnpm add -D unplugin-fontsVite
// vite.config.ts
import Unfonts from 'unplugin-fonts/vite'
export default defineConfig({
plugins: [
Unfonts({ /* options */ }),
],
})Example: examples/vite
Webpack
Warning Not implemented yet
// webpack.config.js
module.exports = {
/* ... */
plugins: [
require('unplugin-fonts/webpack')({ /* options */ })
]
}Nuxt
// nuxt.config.js
export default {
modules: [
['unplugin-fonts/nuxt'],
],
unfonts: {
/* options */
}
}Example: examples/nuxt
SvelteKit
import { sveltekit } from '@sveltejs/kit/vite'
import Unfonts from 'unplugin-fonts/vite'
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
sveltekit(),
Unfonts({
/* options */
})
]
})<script>
// +layout.svelte
import { links } from 'unplugin-fonts/head'
</script>
<svelte:head>
{#each links as link}
<link {...link?.attrs || {}} />
{/each}
</svelte:head>Example: examples/sveltekit
Astro
// astro.config.js
import { defineConfig } from 'astro/config'
import Unfonts from 'unplugin-fonts/astro'
export default defineConfig({
integrations: [
Unfonts({
/* options */
})
]
})---
// src/pages/index.astro
import Unfont from 'unplugin-fonts/astro/component.astro';
---
<html>
<head>
<Unfont />
</head>
<body>
<!-- ... -->
</body>
</html>Example: examples/astro
Migrating from vite-plugin-fonts
// vite.config.ts
-import { VitePluginFonts } from 'vite-plugin-fonts'
+import Unfonts from 'unplugin-fonts/vite'
export default defineConfig({
plugins: [
- VitePluginFonts({
+ Unfonts({
/* options */
}),
],
})// main.ts
-import 'virtual:fonts.css'
+import 'unfonts.css'Note Required if using custom or fontsource providers
import 'unfonts.css'Move @font-face rules from the external CSS bundle into an inline <style> tag in the HTML <head>. This lets the browser discover font URLs immediately without waiting for the stylesheet to load, reducing render-blocking requests and improving LCP.
Unfonts({
inlineFontFace: true,
// ...providers
})Load fonts from Typekit.
Unfonts({
// Typekit API
typekit: {
/**
* Typekit project id
*/
id: '<projectId>',
/**
* customizes the base URL for the font request
* default: 'https://use.typekit.net/'
*/
fontBaseUrl: 'https://use.typekit.net/',
/**
* enable non-blocking renderer
* <link rel="preload" href="xxx" as="style" onload="this.rel='stylesheet'">
* default: true
*/
defer: true,
/**
* define where the font load tags should be inserted
* default: 'head-prepend'
* values: 'head' | 'body' | 'head-prepend' | 'body-prepend'
*/
injectTo: 'head-prepend',
/**
* Declare font families in this Typekit project to generate
* fallback @font-face declarations that reduce layout shift.
*/
families: [{
name: 'Futura PT',
fallback: { category: 'sans-serif' },
}],
},
})Load fonts from Google Fonts.
Unfonts({
// Google Fonts API V2
google: {
/**
* enable preconnect link injection
* <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
* default: true
*/
preconnect: false,
/**
* allow preconnect to be customized
* default: 'https://fonts.gstatic.com'
*/
preconnectUrl: 'https://fonts.gstatic.com',
/**
* customizes the base URL for the font request
* default: 'https://fonts.googleapis.com/css2'
*/
fontBaseUrl: 'https://fonts.googleapis.com/css2',
/**
* values: auto, block, swap(default), fallback, optional
* default: 'swap'
*/
display: 'block',
/**
* define which characters to load
* default: undefined (load all characters)
*/
text: 'ViteAwsom',
/**
* define where the font load tags should be inserted
* default: 'head-prepend'
* values: 'head' | 'body' | 'head-prepend' | 'body-prepend'
*/
injectTo: 'head-prepend',
/**
* Fonts families lists
*/
families: [
// families can be either strings (only regular 400 will be loaded)
'Source Sans Pro',
// or objects
{
/**
* Family name (required)
*/
name: 'Roboto',
/**
* Family styles
*/
styles: 'ital,wght@0,400;1,200',
/**
* enable non-blocking renderer
* <link rel="preload" href="xxx" as="style" onload="this.rel='stylesheet'">
* default: true
*/
defer: true,
/**
* Generate a fallback @font-face to reduce layout shift.
* See "Font Fallback Metrics" section for details.
*/
fallback: {
category: 'sans-serif',
},
},
],
},
})Load custom fonts from files.
Unfonts({
// Custom fonts.
custom: {
/**
* Fonts families lists
*/
families: [{
/**
* Name of the font family.
*/
name: 'Roboto',
/**
* Local name of the font. Used to add `src: local()` to `@font-rule`.
*/
local: 'Roboto',
/**
* Regex(es) of font files to import. The names of the files will
* predicate the `font-style` and `font-weight` values of the `@font-rule`'s.
*/
src: './src/assets/fonts/*.ttf',
/**
* This function allow you to transform the font object before it is used
* to generate the `@font-rule` and head tags.
*/
transform(font) {
if (font.basename === 'Roboto-Bold') {
// update the font weight
font.weight = 700
}
// we can also return null to skip the font
return font
},
/**
* Generate a fallback @font-face to reduce layout shift.
* See "Font Fallback Metrics" section for details.
*/
fallback: {
category: 'sans-serif',
},
}],
/**
* Defines the default `font-display` value used for the generated
* `@font-rule` classes.
*/
display: 'auto',
/**
* Using `<link rel="preload">` will trigger a request for the WebFont
* early in the critical rendering path, without having to wait for the
* CSSOM to be created.
*/
preload: true,
/**
* Using `<link rel="prefetch">` is intended for prefetching resources
* that will be used in the next navigation/page load
* (e.g. when you go to the next page)
*
* Note: this can not be used with `preload`
*/
prefetch: false,
/**
* define where the font load tags should be inserted
* default: 'head-prepend'
* values: 'head' | 'body' | 'head-prepend' | 'body-prepend'
*/
injectTo: 'head-prepend',
},
})Load fonts from Fontsource packages.
Note You will need to install
@fontsource/<font>packages.
Unfonts({
// Fontsource API
fontsource: {
/**
* Fonts families lists
*/
families: [
// families can be either strings (load default font set)
// Require the `@fontsource/abeezee` package to be installed.
'ABeeZee',
'Inter Variable', // Require the `@fontsource-variable/inter` package to be installed.
{
/**
* Name of the font family.
* Require the `@fontsource/roboto` package to be installed.
*/
name: 'Roboto',
/**
* Load only a subset of the font family.
*/
weights: [400, 700],
/**
* Restrict the font styles to load.
*/
styles: ['italic', 'normal'],
/**
* Use another font subset.
*/
subset: 'latin-ext',
},
{
/**
* Name of the font family.
* Require the `@fontsource-variable/cabin` package to be installed.
*/
name: 'Cabin',
/**
* When using variable fonts, you can choose which axes to load.
*/
variable: {
wght: true,
slnt: true,
ital: true,
},
/**
* Generate a fallback @font-face to reduce layout shift.
* See "Font Fallback Metrics" section for details.
*/
fallback: {
category: 'sans-serif',
},
},
],
},
})Reduce Cumulative Layout Shift (CLS) by generating fallback @font-face rules that adjust a local system font to match your web font's dimensions. Powered by fontaine.
When enabled on a font family, the plugin will:
- Generate a companion
@font-facewithsize-adjust,ascent-override,descent-override, andline-gap-overrideto morph a system font (e.g. Arial) to match the web font's size - Automatically rewrite
font-familydeclarations in your CSS to include the fallback
This is opt-in per font family via the fallback option. It works with all providers:
Unfonts({
google: {
families: [
{
name: 'Roboto',
styles: 'wght@400;700',
// Enable fallback for this font
fallback: {
// Font category — used to pick appropriate system fonts
// values: 'sans-serif' | 'serif' | 'monospace'
// default: 'sans-serif'
category: 'sans-serif',
},
},
// String families are unaffected
'Open Sans',
],
},
custom: {
families: [{
name: 'My Font',
src: './src/assets/fonts/*.woff2',
fallback: {
// Explicitly choose which system fonts to use as fallback bases
fallbacks: ['Arial', 'Helvetica Neue'],
// Override the fallback font-face name (default: '{name} fallback')
name: 'My Font Override',
},
}],
},
fontsource: {
families: [{
name: 'Inter',
weights: [400, 700],
fallback: {
category: 'sans-serif',
},
}],
},
typekit: {
id: '<projectId>',
// Typekit requires declaring families to generate fallbacks
families: [{
name: 'Futura PT',
fallback: {
category: 'sans-serif',
},
}],
},
})The generated output looks like this:
/* Generated fallback @font-face */
@font-face {
font-family: "Roboto fallback";
src: local("Arial");
size-adjust: 100.06%;
ascent-override: 92.77%;
descent-override: 24.41%;
line-gap-override: 0%;
}And your CSS font-family: 'Roboto', sans-serif is automatically transformed to font-family: 'Roboto', "Roboto fallback", sans-serif.
| Option | Type | Default | Description |
|---|---|---|---|
category |
'sans-serif' | 'serif' | 'monospace' |
'sans-serif' |
Font category, used to auto-select system fallback fonts |
fallbacks |
string[] |
Auto-detected | Override which system fonts to use as fallback bases |
name |
string |
'{family} fallback' |
Override the generated fallback font-face name |
{
"compilerOptions": {
"types": ["unplugin-fonts/client"]
}
}or
// declaration.d.ts
/// <reference types="unplugin-fonts/client" />- https://web.dev/optimize-webfont-loading/
- https://csswizardry.com/2020/05/the-fastest-google-fonts/
- https://github.com/unjs/fontaine - Font metric override library used for fallback generation
- (unmaintained) https://www.npmjs.com/package/webfontloader