Core Web Vitals Optimization
Step-by-step guide to optimizing Core Web Vitals. Improve your website's LCP, FID, and CLS for better rankings and user experience.
Core Web Vitals have been an official Google ranking factor since 2021. In this guide, I’ll show you how to systematically optimize the three core metrics – with concrete actions and code examples.
What Are Core Web Vitals?
Google measures user experience with Core Web Vitals using three metrics:
| Metric | Description | Good | Needs Improvement | Poor |
|---|---|---|---|---|
| LCP | Largest Contentful Paint | ≤ 2.5s | ≤ 4.0s | > 4.0s |
| FID/INP | First Input Delay / Interaction to Next Paint | ≤ 100ms | ≤ 300ms | > 300ms |
| CLS | Cumulative Layout Shift | ≤ 0.1 | ≤ 0.25 | > 0.25 |
1. Optimizing LCP (Largest Contentful Paint)
LCP measures how long it takes until the largest visible element is loaded.
Common LCP Problems
- Slow server response
- Render-blocking resources
- Non-optimized images
- Client-Side Rendering
Solution: Optimize Server Response
Improve TTFB (Time to First Byte):
# Nginx configuration
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# Browser caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
PHP Optimizations:
- Enable OPcache
- Optimize database queries
- Use caching layer (Redis, Memcached)
Solution: Optimize Images
Use modern image formats:
<picture>
<source srcset="/image.avif" type="image/avif">
<source srcset="/image.webp" type="image/webp">
<img src="/image.jpg" alt="Description" width="800" height="600">
</picture>
Prioritize LCP image:
<!-- Load hero image with high priority -->
<link rel="preload" as="image" href="/hero.webp" type="image/webp">
<img
src="/hero.webp"
alt="Hero"
fetchpriority="high"
loading="eager"
>
Solution: Inline Critical CSS
<head>
<style>
/* Critical CSS for above-the-fold */
body { margin: 0; font-family: sans-serif; }
.hero { height: 100vh; display: flex; align-items: center; }
.hero h1 { font-size: 3rem; }
</style>
<!-- Load remaining CSS asynchronously -->
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>
2. Optimizing FID/INP (Interactivity)
FID measures the time from the first user interaction to the browser’s response. INP (Interaction to Next Paint) is replacing FID as the new metric and measures all interactions.
Common Interactivity Problems
- Long JavaScript execution
- Blocking third-party scripts
- Unnecessary JavaScript bundles
- Hydration in SSR frameworks
Solution: Split JavaScript
Code-Splitting with dynamic imports:
// Instead of loading everything immediately
import { heavyFunction } from './heavy-module.js';
// Load only when needed
const button = document.querySelector('#action-button');
button.addEventListener('click', async () => {
const { heavyFunction } = await import('./heavy-module.js');
heavyFunction();
});
Solution: Avoid Long Tasks
Split tasks into smaller units:
// Bad: One long task
function processItems(items) {
items.forEach(item => processItem(item)); // Blocks the main thread
}
// Better: With yielding
async function processItemsWithYielding(items) {
for (const item of items) {
processItem(item);
// Give the browser time for other tasks
if (shouldYield()) {
await scheduler.yield();
}
}
}
function shouldYield() {
return performance.now() - taskStartTime > 50; // 50ms chunks
}
Solution: Optimize Third-Party Scripts
<!-- Defer for non-critical scripts -->
<script defer src="https://analytics.example.com/script.js"></script>
<!-- Or dynamic loading after user interaction -->
<script>
let analyticsLoaded = false;
function loadAnalytics() {
if (analyticsLoaded) return;
analyticsLoaded = true;
const script = document.createElement('script');
script.src = 'https://analytics.example.com/script.js';
document.body.appendChild(script);
}
// Load after first interaction or after 5 seconds
['scroll', 'click', 'touchstart'].forEach(event => {
window.addEventListener(event, loadAnalytics, { once: true });
});
setTimeout(loadAnalytics, 5000);
</script>
3. Optimizing CLS (Layout Stability)
CLS measures how much elements shift during loading.
Common CLS Problems
- Images without dimensions
- Dynamically inserted content
- Web fonts (FOIT/FOUT)
- Ads and embeds
Solution: Specify Dimensions for Media
<!-- Always specify width and height -->
<img
src="/image.jpg"
alt="Description"
width="800"
height="600"
>
<!-- Or with aspect-ratio in CSS -->
<style>
.responsive-image {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
}
</style>
Solution: Placeholders for Dynamic Content
/* Fixed height for ad banners */
.ad-container {
min-height: 250px;
background: #f0f0f0;
}
/* Skeleton screens for loading times */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
Solution: Optimize Web Fonts
/* font-display: swap prevents FOIT */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap;
}
/* Optional: Fallback with similar metrics */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap;
size-adjust: 105%; /* Adjustment to system font */
}
Font preloading:
<link
rel="preload"
href="/fonts/custom.woff2"
as="font"
type="font/woff2"
crossorigin
>
Measurement and Monitoring
Tools for Core Web Vitals
- PageSpeed Insights – Quick test with recommendations
- Google Search Console – Real user data (CrUX)
- Chrome DevTools – Performance tab, Lighthouse
- Web Vitals Extension – Real-time measurement while browsing
Real User Monitoring (RUM)
// Include Web Vitals library
import { onCLS, onFID, onLCP, onINP } from 'web-vitals';
function sendToAnalytics({ name, value, rating }) {
// Send to your analytics system
gtag('event', name, {
value: Math.round(name === 'CLS' ? value * 1000 : value),
event_category: 'Web Vitals',
event_label: rating,
non_interaction: true,
});
}
onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
Prioritization: What to Optimize First?
Order by impact:
- LCP – Greatest influence on perceived speed
- CLS – Frustrates users the most
- FID/INP – Important for interactive pages
Identify quick wins:
| Problem | Effort | Impact |
|---|---|---|
| Optimize images | Low | High |
| Lazy loading | Low | Medium |
| Font optimization | Low | Medium |
| Code splitting | Medium | High |
| Server-side rendering | High | Very high |
Conclusion
Optimizing Core Web Vitals isn’t a one-time project but a continuous process. Start with quick wins and work your way to more complex optimizations.
Key takeaways:
- Measure regularly – both lab and field data
- Prioritize by impact and effort
- Test changes on staging environments
- Monitor effects after deployment
Need help with performance optimization? I analyze your website and develop a customized optimization plan.
Related Content
- Performance Optimization - My service for faster websites
- SEO Optimization - Better rankings through performance
- Hosting & Maintenance - Fast servers for optimal Core Web Vitals
Tags:
René Lauenroth
Web developer with over 30 years of experience. Specialized in TYPO3, WordPress and AI integration.
Learn moreRelated Articles
CSS Background Effects: Parallax & Patterns
Master modern CSS background techniques: parallax effects, scrolling patterns in containers, gradient overlays, and responsive backgrounds. With browser compatibility and performance tips.
SEOLocal SEO for Freelancers
Local SEO strategies for freelancers and self-employed professionals. Google Business Profile, business directories, and NAP consistency explained simply.
Web DevelopmentAstro Framework: Static Sites
Discover why the Astro Framework is the best choice for high-performance websites. Islands Architecture, Zero-JS-Default and practical examples.
Have questions about this topic?
I'm happy to advise you on your web project.
Get in touch