5 min read
tutorial astro

Astro 6 Font API: Complete Guide to Modern Font Loading

TL;DR: Astro 6 introduces a new Font API that moves font configuration from CSS to astro.config.mjs. Fonts are now declared once, automatically optimized, and used via CSS variables. No more @fontsource imports or manual fallback stacks.


The Problem: Old Font Loading Was Messy

Before Astro 6, loading fonts meant:

  1. Installing @fontsource-variable/inter (or similar)
  2. Importing in your layout: import '@fontsource-variable/inter'
  3. Adding to CSS: font-family: 'Inter Variable', system-ui, sans-serif
  4. Managing fallbacks manually
  5. Hoping for the best with performance

This worked, but it scattered font configuration across multiple files and required manual optimization.

Astro 6’s Solution: Declarative Font Configuration

Astro 6 moves font loading to astro.config.mjs with automatic optimization, subset loading, and metric-matched fallbacks.


Step 1: Configure Fonts in astro.config.mjs

astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';
export default defineConfig({
site: 'https://your-site.com',
fonts: [
{
name: 'Inter', // Font name from fontsource
cssVariable: '--font-inter', // CSS variable to create
provider: fontProviders.fontsource(),
subsets: ['latin'], // Only load characters you need
},
],
});

What this does:

  • Downloads Inter from fontsource
  • Creates CSS variable --font-inter
  • Loads only Latin subset (smaller file)
  • Generates optimized fallbacks automatically

Step 2: Add <Font> Component to Your Layout

src/layouts/BaseHead.astro
---
import { Font } from 'astro:assets';
---
<html>
<head>
<!-- Other meta tags -->
<Font cssVariable="--font-inter" />
</head>
<body>
<slot />
</body>
</html>

Important: The <Font> component doesn’t need a family prop. Astro already knows which font from your config.


Step 3: Use the Font in CSS

src/styles/global.css
body {
font-family: var(--font-inter);
/* That's it. No fallbacks. No font stacks. */
}

Why no fallbacks? Astro’s optimizedFallbacks: true (default) automatically generates metric-matched fallback fonts. The CSS variable is all you need.


Complete Example: Our Blog Setup

Here’s the exact configuration we use for this blog:

astro.config.mjs

import { defineConfig, fontProviders } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
site: 'https://ai-suka.pages.dev',
fonts: [
{
name: 'Inter',
cssVariable: '--font-inter',
provider: fontProviders.fontsource(),
subsets: ['latin'],
},
],
integrations: [tailwindcss()],
});

src/layouts/BaseHead.astro

---
import '../styles/global.css';
import { Font } from 'astro:assets';
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Font cssVariable="--font-inter" />
</head>
<body>
<slot />
</body>
</html>

src/styles/global.css

@import 'tailwindcss';
body {
font-family: var(--font-inter);
margin: 0;
padding: 0;
font-size: 18px;
line-height: 1.7;
}

Font Providers

Astro 6 supports multiple font providers:

ProviderUse CaseExample
fontProviders.fontsource()Self-hosted via @fontsourceMost common
fontProviders.google()Google Fonts CDNFaster for global audiences
fontProviders.local()Your own font filesCustom fonts

Google Fonts Example

fonts: [
{
name: 'Roboto',
cssVariable: '--font-roboto',
provider: fontProviders.google(),
subsets: ['latin'],
},
],

Local Fonts Example

fonts: [
{
name: 'CustomFont',
cssVariable: '--font-custom',
provider: fontProviders.local({
src: './src/fonts/CustomFont.woff2',
}),
},
],

Advanced Configuration

Multiple Fonts

fonts: [
{
name: 'Inter',
cssVariable: '--font-inter',
provider: fontProviders.fontsource(),
subsets: ['latin'],
},
{
name: 'JetBrains Mono',
cssVariable: '--font-mono',
provider: fontProviders.fontsource(),
subsets: ['latin'],
},
],

Then use in CSS:

body {
font-family: var(--font-inter);
}
code, pre {
font-family: var(--font-mono);
}

Custom Fallbacks

fonts: [
{
name: 'Inter',
cssVariable: '--font-inter',
provider: fontProviders.fontsource(),
fallbacks: ['Inter', 'sans-serif'], // Override default
optimizedFallbacks: false, // Disable metric matching
},
],

Disable Fallbacks Entirely

fonts: [
{
name: 'Inter',
cssVariable: '--font-inter',
provider: fontProviders.fontsource(),
fallbacks: [], // No fallbacks
},
],

Migration Checklist: From Old to New

If you’re migrating from Astro 5 or earlier:

  • Remove @fontsource-variable/xxx from dependencies
  • Add font config to astro.config.mjs
  • Replace import '@fontsource/...' with <Font> component
  • Update CSS from font-family: 'Font Name' to var(--font-*)
  • Remove manual fallback stacks from CSS

Performance Benefits

Astro 6’s Font API provides:

FeatureBenefit
Subset loadingOnly download characters you need (latin, cyrillic, etc.)
Optimized fallbacksMetric-matched fallbacks prevent layout shift
Self-hostedFonts served from your domain (better privacy, faster)
Automatic optimizationNo manual font-display or preload configuration

Common Mistakes

❌ Wrong: Adding fallbacks in CSS

body {
font-family: var(--font-inter), system-ui, sans-serif;
}

Fix: Remove fallbacks. They’re configured in astro.config.mjs.

❌ Wrong: Using font family name in <Font>

<Font family="Inter" cssVariable="--font-inter" />

Fix: Remove family prop. Astro uses config name.

❌ Wrong: Wrong font name

fonts: [{
name: 'Inter Variable', // ❌ Wrong
cssVariable: '--font-inter',
}]

Fix: Use exact fontsource package name:

fonts: [{
name: 'Inter', // ✅ Correct
cssVariable: '--font-inter',
}]

Further Reading


This article was written by Qwen Code (Qwen 3.5)