Embedding Fonts as Data URIs in CSS
Learn how to embed WOFF and WOFF2 font files as data URIs in @font-face declarations to eliminate font loading flash and improve First Contentful Paint.
Detailed Explanation
Inline Fonts with Data URIs
Embedding web fonts as data URIs inside @font-face CSS declarations eliminates the font file request entirely. The font data is available as soon as the CSS is parsed, which can dramatically reduce Flash of Invisible Text (FOIT) and Flash of Unstyled Text (FOUT).
@font-face with Data URI
@font-face {
font-family: 'CustomFont';
src: url('data:font/woff2;base64,d09GMgABAAAAADw...') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
When Font Inlining Makes Sense
Font inlining is most effective in these scenarios:
- Critical text rendering — When the font is needed above the fold and FOIT is unacceptable
- Single-page applications — When the CSS is already bundled and cached
- Small icon fonts — Subset icon fonts under 20 KB compress well as data URIs
- Email templates — Where external font requests may be blocked
Font Format Recommendations
| Format | MIME Type | Typical Size | Support |
|---|---|---|---|
| WOFF2 | font/woff2 |
Smallest | Modern browsers |
| WOFF | font/woff |
Small | Wide support |
| TTF | font/ttf |
Large | Universal |
Always prefer WOFF2 for data URIs because its compression produces the smallest Base64 payload.
Subsetting for Smaller Data URIs
Before inlining a font, subset it to include only the characters you need. A full Google Fonts file for Latin + Cyrillic might be 60 KB, but a subset with only Latin characters could be 15 KB. Tools like pyftsubset and Google Fonts' text parameter can generate subsets.
Trade-offs
- Pro: Eliminates font file request, faster rendering
- Pro: No CORS configuration needed
- Con: Cannot be cached independently from CSS
- Con: Increases CSS file size by ~33%
- Con: If font changes, entire CSS cache is invalidated
Use Case
You are building a high-performance landing page where First Contentful Paint is critical, and you want the hero heading font to be available instantly without any font loading delay.