Skip to content

Experimental fonts API

Added in: astro@5.7.0 Beta

This experimental feature allows you to use fonts from your filesystem and various providers through a unified API:

astro.config.mjs
import { defineConfig } from 'astro/config'
export default defineConfig({
experimental: {
fonts: {
families: ['Roboto']
}
}
})
src/components/Head.astro
---
import { Font } from 'astro:assets'
---
<Font family='Roboto' preload />
<style>
body {
font-family: var(--astro-font-roboto);
}
</style>

Configuring experimental fonts

Section titled Configuring experimental fonts

To start using fonts, register a font family in experimental.fonts.families:

astro.config.mjs
import { defineConfig } from 'astro/config'
export default defineConfig({
experimental: {
fonts: {
families: ['Roboto']
}
}
})

Configuring a remote font family

Section titled Configuring a remote font family

A font family can be registered as a string or an object:

astro.config.mjs
import { defineConfig } from 'astro/config'
export default defineConfig({
experimental: {
fonts: {
families: [
'Roboto', // Shortand for { name: 'Roboto' }
{
name: 'Lato'
}
]
}
}
})

It accepts various options:

The required font family name:

astro.config.mjs
{
name: 'Roboto'
}

An optional alias name. This is useful to avoid conflicts, especially as an integration:

astro.config.mjs
{
name: 'Roboto',
as: 'Custom'
}

An optional provider name. Defaults to google and accepts one of these values: google, local or any name from other registered providers:

astro.config.mjs
{
providers: [custom()],
families: [
{
name: 'Roboto',
provider: 'custom'
}
]
}

An optional array of font weights (strings and numbers are allowed). Defaults to ['400']:

astro.config.mjs
{
name: 'Roboto',
weights: [200, '300', 'bold']
}

An optional array of font styles. Defaults to ['normal', 'italic']:

astro.config.mjs
{
name: 'Roboto',
styles: ['oblique']
}

An optional array of font subsets. Defaults to ['cyrillic-ext', 'cyrillic', 'greek-ext', 'greek', 'vietnamese', 'latin-ext', 'latin']:

astro.config.mjs
{
name: 'Roboto',
subsets: ['latin']
}

An optional array of font fallbacks:

astro.config.mjs
{
name: 'Roboto',
fallbacks: ['Custom', 'sans-serif']
}

If the latest fallback if a generic family name, Astro will try to generate an optimized fallback using font metrics.

An optional boolean flag to enable/disable the automatic fallback generation described above. Defaults to false:

astro.config.mjs
{
name: 'Roboto',
fallbacks: ['Arial', 'sans-serif'],
automaticFallback: false
}

An optional font display:

astro.config.mjs
{
name: 'Roboto',
display: 'swap'
}

An optional unicode range:

astro.config.mjs
{
name: 'Roboto',
unicodeRange: 'U+26'
}

An optional font stretch:

astro.config.mjs
{
name: 'Roboto',
stretch: 'condensed'
}

Optional font feature settings:

astro.config.mjs
{
name: 'Roboto',
featureSettings: '"smcp" 2'
}

Optional font variation settings:

astro.config.mjs
{
name: 'Roboto',
variationSettings: '"xhgt" 0.7'
}

More providers can be used by registering them in fonts.providers:

astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config'
export default defineConfig({
experimental: {
fonts: {
providers: [fontProviders.fontsource()],
families: [{
name: 'Roboto',
provider: 'fontsource'
}]
}
}
})

fontProviders supports most unifont providers (except google since it’s built-in):

  • adobe
  • bunny
  • fontshare
  • fontsource

To use local fonts, set provider: 'local' and specify some sources:

astro.config.mjs
import { defineConfig } from 'astro/config'
export default defineConfig({
experimental: {
fonts: {
families: [{
name: 'Roboto',
provider: 'local',
src: [{
paths: ['./src/assets/fonts/roboto.woff2']
}]
}]
}
}
})

Here are the configuration differences with a remote font family:

  • All options from remote font families except weights, styles and subsets
  • There’s a required src option

A required option of sources for your font. It’s an array of objects with the following properties:

  • All options from remote font families except fallbacks, automaticFallback and subsets
  • paths: An array of paths relative to project root

Once you’ve configured the fonts in your config, you can start using them by calling the <Font /> component and consuming the CSS variable.

astro:assets exports a <Font /> component:

src/components/Head.astro
---
import { Font } from 'astro:assets'
---
<Font family='Roboto' />

It accepts various options:

A required font family name.

An optional boolean to generate preload links. Defaults to false:

src/components/Head.astro
---
import { Font } from 'astro:assets'
---
<Font family='Roboto' preload />

The <Font /> component registers some styles, including CSS variables:

src/components/Head.astro
<style>
body {
font-family: var(--astro-font-roboto);
}
</style>

Generated CSS variables follow the --astro-font-name pattern, where name is converted to kebab case.

Creating a custom font provider

Section titled Creating a custom font provider

An Astro font provider is made of 2 parts: the config object and the actual implementation.

You can use the defineFontProvider() type helper:

provider/config.ts
import { defineFontProvider } from 'astro/config'
interface Config {
// ...
}
export function myProvider(config: Config) {
return defineFontProvider({
name: 'my-provider',
entrypoint: new URL('./implementation.js', import.meta.url),
config
})
}

The file linked by the the entrypoint must contain a named provider export, which is a unifont provider:

import { defineFontProvider } from "unifont"
export const provider = defineFontProvider("my-provider", async (options, ctx) => {
// ...
})

Registering fonts as an integration

Section titled Registering fonts as an integration

As an integration, you may want to use fonts but you want to avoid conflicts with the user. Eg. both the user and your integration use Roboto but with different settings.

To make sure there’s no conflict, use the as prop and make sure to make it specific enough:

my-integration.ts
updateConfig({
experimental: {
fonts: {
families: [
{
name: 'Roboto',
as: 'CustomIntegration: Roboto',
// ...
}
]
}
}
})

To get proper autocompletion when using the <Font /> component, you’ll need to use module augmentation:

env.d.ts
declare module 'astro:assets' {
export type FontFamily = 'CustomIntegration: Roboto'
}

In order to avoid downloading files if not needed, caching has been implemented.

  • If a font file is requested, it will first check if it has been downloaded locally and load it. Otherwise it will make a network request
  • Font files are downloaded on demand. We only download them if they’re requested by the browser
  • They are not cached in the browser however, to avoid getting stale assets
  • To clear the cache, remove the .astro/fonts directory
  • All font files are requested. For each file, it will first check if it has been downloaded locally and load it. Otherwise it will make a network request
  • Font files are copied to the _astro/fonts output directory, so they can benefit from HTTP caching of static assets
  • To clear the cache, remove the node_modules/.astro/font directory

For full details and to give feedback on this experimental API, see the Fonts RFC.

Contribute Community Sponsor