
Introduction
Fonts play a crucial role in web design. A beautiful typeface enhances readability, aesthetics, and brand perception but when fonts aren’t optimized, they can become one of the biggest performance bottlenecks in your site’s rendering pipeline.
When a webpage loads, browsers often need to fetch custom web fonts from external servers. During this process, users may experience one of two phenomena: FOIT (Flash of Invisible Text) or FOUT (Flash of Unstyled Text). Both impact perceived performance and user experience differently.
To address these rendering challenges, CSS introduced the font-display property a powerful tool that gives developers control over how and when fonts are displayed.
In this article, we’ll explore what FOIT and FOUT are, how the font-display property mitigates them, show real-world code examples, and share best practices for creating beautiful yet performant typography on the web.
What Is FOIT (Flash of Invisible Text)?

FOIT, or Flash of Invisible Text, occurs when the browser hides text while waiting for a custom font to load. The result? A blank or invisible text area for several seconds before the font finally appears.
This happens because browsers, by default, avoid showing fallback fonts to prevent layout shifts once the custom font is loaded but it also delays content visibility.
Example
<style>
@font-face {
font-family: 'MyFont';
src: url('/fonts/MyFont.woff2') format('woff2');
/* No font-display specified – FOIT likely */
}
body {
font-family: 'MyFont', sans-serif;
}
</style>
<p>Hello, world! Watch the delay as the font loads...</p>
If the custom font takes too long to load, users may see nothing for up to 3 seconds or more (depending on browser timeout settings).
Downside of FOIT
- Poor perceived performance
- Invisible content, leading to frustration
- Bad accessibility. Users with slower networks are left with blank screens
What Is FOUT (Flash of Unstyled Text)?

FOUT, or Flash of Unstyled Text, happens when the browser initially renders the text using a fallback system font, and then replaces it with the custom font once it’s ready.
While this causes a visual flash or shift, the user at least sees readable content immediately — making FOUT a better tradeoff for performance and usability.
Example
<style>
@font-face {
font-family: 'MyFont';
src: url('/fonts/MyFont.woff2') format('woff2');
font-display: swap; /* Enables FOUT behavior */
}
body {
font-family: 'MyFont', Arial, sans-serif;
}
</style>
<p>The text appears right away with a fallback font, then swaps smoothly.</p>
In this case, the browser shows system text first (e.g., Arial), and then seamlessly replaces it with MyFont when loaded ensuring immediate content visibility.
Why FOUT Is Often Preferred
- Better perceived performance
- Content visible immediately
- Minor layout shift (can be minimized using font metrics)
Font Display Strategies (font-display Property)
The font-display descriptor in @font-face allows developers to control how fonts behave during the loading phase. It provides balance between performance, readability, and visual stability.
Available Values
font-display: auto;
Default behavior, typically FOIT for most browsers.
Example:
@font-face {
font-family: 'MyFont';
src: url('/fonts/MyFont.woff2') format('woff2');
font-display: auto;
}
font-display: block;
The browser waits for a short block period (~3s). If the font isn’t loaded, text becomes invisible (FOIT). Once ready, it appears.
font-display: swap;
Shows fallback text immediately (FOUT) and swaps to the custom font when ready.
font-display: swap;
font-display: fallback;
Shorter block period (~100ms). If the font doesn’t load fast, the fallback remains. Reduces shifts.
font-display: optional;
Ideal for slow connections if the font loads instantly, great; otherwise, fallback stays.
Example with Multiple Fonts
<style>
@font-face {
font-family: 'Lora';
src: url('/fonts/Lora.woff2') format('woff2');
font-display: fallback;
}
@font-face {
font-family: 'OpenSans';
src: url('/fonts/OpenSans.woff2') format('woff2');
font-display: swap;
}
body {
font-family: 'Lora', 'OpenSans', serif;
}
</style>
This allows different behaviors for different fonts, optimizing UX across devices and network conditions.
FOIT vs FOUT vs Font-Display: Key Differences
FOIT hides text completely until the custom font is ready, resulting in invisible content during loading. FOUT, by contrast, displays text immediately with a fallback font, then replaces it once the custom font downloads.
The font-display property gives you fine-grained control over these behaviors, allowing you to choose between immediate visibility (FOUT) or stylistic precision (FOIT).
Developers today prefer font-display: swap or font-display: fallback as the best middle ground between design consistency and performance. These strategies ensure that text is always visible and that layout shifts are minimal.
Best Practices for Font Optimization
- Use Modern Formats like WOFF2
WOFF2 offers smaller file sizes and faster decompression times than TTF or OTF. - Subset Your Fonts
Don’t load entire character sets if your site doesn’t need them. Use tools like glyphhanger to extract only necessary characters. - Host Fonts Locally
Self-hosting fonts ensure better caching and eliminates third-party latency.
4. Preload Critical Fonts
<link rel="preload" href="/fonts/Lora.woff2" as="font" type="font/woff2" crossorigin>
This helps browsers prioritize font loading before render-blocking resources.
5. Combine with font-display: swap
Immediate fallback rendering with seamless replacement improves perceived performance.
6. Use System Fonts When Possible
For UI-heavy or enterprise apps, system fonts (like -apple-system, Roboto, Segoe UI) ensure instant rendering.
Tools for Measuring Font Performance
You can measure font loading performance using tools such as:
- Lighthouse (Google Chrome DevTools) – evaluates render-blocking fonts
- WebPageTest – provides detailed waterfall views
- Chrome Performance Panel – visualize FOIT/FOUT behavior
- FontFaceSet API – measure font load events in code
Example Using JavaScript
if (document.fonts) {
document.fonts.ready.then(() => {
console.log("All fonts have loaded successfully!");
});
}
This API lets developers detect when fonts finish loading and trigger fallback handling or transitions.
Common Pitfalls & Challenges
- Unoptimized font sizes – loading large font files slows down initial paint.
- Too many weights or styles – limit to 2–3 essential variants.
- Not using preloading – can delay render by 300–600ms.
- Neglecting fallback styles – can cause visible shifts and poor UX.
- Ignoring accessibility – users on slow networks or text readers may miss content during FOIT.
Conclusion
Font optimization is as much about perceived performance as it is about visual design. FOIT, FOUT, and font-display strategies each influence how users experience text while fonts are loading.
FOIT prioritizes aesthetics but risks blank screens; FOUT ensures instant readability at the cost of visual consistency; and font-display provides the developer with precise control to balance both.
By combining smart font loading strategies like font-display: swap, preloading critical fonts, and subsetting unused characters you can deliver both speed and beauty.
In a digital world where milliseconds impact engagement and SEO rankings, optimizing font delivery isn’t just good practice it’s essential for crafting fast, accessible, and visually polished web experiences.


