How Self Hosted Fonts Can Fix Your LCP Score
3 min read
In this guide, we dive into how fonts contribute to layout instability, and walk through optimizing performance with self-hosted fonts. Fewer shifts. Faster loads. Better UX.
By Nikolina Požega
1. Introduction: Understanding Layout Shift and LCP
When measuring website performance, especially on mobile, Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS) are key Core Web Vitals. A high LCP (e.g., 5 seconds) can hurt SEO and frustrate users. Layout shifts can delay the appearance of the main content, making the page feel sluggish or unstable.
In many cases, the culprit is surprisingly simple: web fonts.
2. Common Causes of Layout Shift
-
Delayed font rendering (FOIT – Flash of Invisible Text)
-
Fallback-to-custom font swap (FOUT – Flash of Unstyled Text)
-
Late-loading animations or images
-
No height reserved for content (e.g. Lottie animations, hero banners)
If your hero section shows up first, but fonts swap in after a second or two — you're likely causing layout shift.
3. How Fonts Cause Layout Shift
When using services like Google Fonts, the browser initially renders content with a fallback font, then swaps in the actual web font once downloaded. This swap changes spacing and pushes elements around — leading to layout shift.
Even using font-display: swap helps perceived performance, but doesn’t fully prevent layout shift.
4. Ways to Load Fonts — And Their Impact
Option A: Google Fonts (default)
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap" rel="stylesheet">
Easy to use, but introduces third-party requests, layout shift, and delays.
Option B: fontsource package (via npm)
import '@fontsource/poppins/400.css';
import '@fontsource/poppins/500.css';
Better than Google Fonts in terms of control, but adds CSS to bundle and still not ideal if full subset isn’t used.
Option C: Self-hosted fonts (recommended)
This gives full control, reduces layout shift, and improves LCP.
5. The Optimal Approach: Self-Hosted Fonts
Step 1: Get Your Fonts
Use Transfonter:
-
Upload your .ttf files or generate them via Google Fonts download
-
Select only needed weights (e.g., 400, 500, 700)
-
Choose subsets: Latin + Latin-ext (important for non-English characters like Č, Ć, Ž)
-
Choose formats: .woff2 (modern) and .woff (fallback)
-
Enable font-display: swap
-
Download the ZIP
Step 2: Place Fonts in Your Public Folder
Example structure:
/public/fonts/Poppins-Regular.woff2
/public/fonts/Poppins-Medium.woff2
Step 3: Create a CSS file (fonts.css)
@font-face {
font-family: 'Poppins';
src: url('/fonts/Poppins-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
font-family: 'Poppins';
src: url('/fonts/Poppins-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
Then import in your app:
import '../styles/fonts.css';
6. Testing the Results
Use these tools:
-
Chrome DevTools → Performance tab → Record page load
-
Lighthouse / PageSpeed Insights → See updated LCP & CLS scores
-
WebPageTest → Analyze layout shifts frame by frame
Watch for:
-
No font fallback flashing
-
No layout shift when fonts load
-
LCP under 2.5s (for good mobile experience)
Final Thoughts
Fonts aren't just about aesthetics — they cary performance power move if implemented correctly. With minimal effort, you eliminate layout shifts, speed up your LCP, and provide a visually stable experience from the first render.
This one optimization could shave over a second from your LCP. Try it today and say goodbye to flash, flicker, and frustration.
