How to Use @font-face in CSS: Complete Guide
The @font-face CSS at-rule is how you embed custom fonts on your website. It tells the browser where to find font files, what to call them, and how to handle loading. This guide covers everything you need to know — from basic syntax to advanced performance optimization.
Basic @font-face Syntax
At its simplest, a @font-face declaration defines a font-family name and the source URL of the font file:
@font-face {
font-family: 'MyCustomFont';
src: url('fonts/MyCustomFont-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
Once declared, you can use this font anywhere in your CSS:
body {
font-family: 'MyCustomFont', -apple-system, sans-serif;
}
Format Fallbacks
To maximize browser compatibility, you can list multiple font formats. The browser will use the first format it supports:
@font-face {
font-family: 'MyCustomFont';
src: url('MyCustomFont.woff2') format('woff2'),
url('MyCustomFont.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
In 2026, WOFF2 + WOFF covers virtually all browsers. For details on when you might need additional formats, see our WOFF2 vs WOFF format guide.
Multiple Weights and Styles
For a font family with multiple weights and styles, create a separate @font-face block for each combination. This ensures browsers can correctly match weights and styles:
/* Regular */
@font-face {
font-family: 'MyCustomFont';
src: url('MyCustomFont-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* Bold */
@font-face {
font-family: 'MyCustomFont';
src: url('MyCustomFont-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* Italic */
@font-face {
font-family: 'MyCustomFont';
src: url('MyCustomFont-Italic.woff2') format('woff2');
font-weight: 400;
font-style: italic;
font-display: swap;
}
Important: Use the same font-family name for all weights/styles. The browser uses the font-weight and font-style descriptors to select the correct file. Do not create separate family names like 'MyFont-Bold'.
The font-display Property
The font-display property controls what happens while the custom font is loading. This is critical for user experience and Core Web Vitals:
| Value | Behavior | Best For |
|---|---|---|
swap |
Shows fallback font immediately, swaps when custom font loads | ✅ Most websites (recommended) |
block |
Hides text briefly (~3s), then shows fallback if font hasn't loaded | Icon fonts, logo text |
fallback |
Short block period (~100ms), then shows fallback | Body text with small fonts |
optional |
Browser decides based on connection speed | Non-critical decorative fonts |
auto |
Browser default (usually similar to block) |
Not recommended |
Preloading Critical Fonts
For fonts used in above-the-fold content (headings, hero text), use <link rel="preload"> to tell the browser to fetch them as early as possible:
<link rel="preload"
href="/fonts/MyCustomFont-Bold.woff2"
as="font"
type="font/woff2"
crossorigin>
Use preloading sparingly — only for 1–2 critical font files that affect LCP. Preloading too many fonts can actually hurt performance by competing for bandwidth with other critical resources.
Local Font Matching
If the user already has the font installed on their system, you can skip the download entirely using the local() function:
@font-face {
font-family: 'MyCustomFont';
src: local('My Custom Font'),
local('MyCustomFont-Regular'),
url('MyCustomFont-Regular.woff2') format('woff2'),
url('MyCustomFont-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
The Webfont Generator has a "Add Local Rule" toggle that automatically includes local() declarations in the generated CSS.
FOIT vs FOUT: Understanding Flash Problems
When using custom web fonts, you'll encounter two common rendering issues:
- FOIT (Flash of Invisible Text): Text is completely hidden until the custom font loads. This happens with
font-display: blockand can feel broken to users. - FOUT (Flash of Unstyled Text): A fallback font is shown first, then swapped to the custom font. This is what
font-display: swapproduces. While there's a visible flash, text is always readable.
FOUT is almost always preferable to FOIT — users can read your content immediately, and the swap is usually subtle. This is why font-display: swap is the recommended default.
Self-Hosting vs. Google Fonts CDN
While Google Fonts is convenient, self-hosting your web fonts has several advantages:
| Factor | Self-Hosted Fonts | Google Fonts CDN |
|---|---|---|
| Privacy | ✅ No third-party tracking | ❌ Google collects visitor data |
| Performance | ✅ Same-origin, no DNS lookup | ⚠️ Extra DNS + connection time |
| GDPR Compliance | ✅ No third-party data transfer | ❌ Potential legal issues in EU |
| Reliability | ✅ Independent of third-party uptime | ⚠️ Depends on Google CDN |
| Control | ✅ Full control over subsetting & caching | ❌ Google decides what to serve |
Use a webfont generator to convert and subset Google Fonts (or any font) for self-hosting — you get better performance, full privacy, and GDPR compliance.
Complete Production Example
Here's a production-ready setup for a two-weight font system with preloading, fallbacks, and proper font stacking:
<!-- In your HTML <head> -->
<link rel="preload" href="/fonts/Inter-Regular.woff2"
as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/Inter-Bold.woff2"
as="font" type="font/woff2" crossorigin>
<style>
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Regular.woff2') format('woff2'),
url('/fonts/Inter-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Bold.woff2') format('woff2'),
url('/fonts/Inter-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
}
</style>
Checklist for Production
- ✅ Convert fonts to WOFF2 (with WOFF fallback)
- ✅ Subset to only the characters you need
- ✅ Use
font-display: swap - ✅ Preload 1–2 critical font files
- ✅ Self-host for privacy and performance
- ✅ Include proper
font-weightandfont-styledescriptors - ✅ Test with real content before deploying