Part of a Series
Web Development: Complete Guide for Business Owners
This article is part of the Web Development content cluster. Explore the complete guide for in-depth coverage of this topic.
View complete guide →If you run a website in 2025, you have likely heard the term "Core Web Vitals" thrown around in SEO discussions, developer forums, or Google Search Console notifications. These metrics have evolved from a nice-to-have ranking signal into a fundamental pillar of modern web performance. Google officially rolled them out as a ranking factor in 2021, and they have only grown in importance since. As a senior full-stack developer who has optimized dozens of sites for these metrics, I can tell you firsthand: fixing Core Web Vitals is not just about pleasing an algorithm. It is about delivering a genuinely fast, stable, and responsive experience to every person who visits your site. This guide is written for developers, site owners, and SEO professionals who want a comprehensive, actionable roadmap to optimizing Core Web Vitals in 2025. By the end of this article, you will understand exactly what these metrics measure, how to diagnose issues, and how to implement battle-tested fixes that will move the needle on your scores.
What Are Core Web Vitals?
Core Web Vitals are a set of three real-world metrics that Google uses to measure user experience on the web. They are part of Google's broader "Page Experience" signal and focus specifically on loading speed, interactivity, and visual stability. Here are the three metrics with their current thresholds:
LCP — Largest Contentful Paint
LCP measures the time it takes for the largest visible element (usually an image, video, or large text block) to render within the viewport. A good LCP is 2.5 seconds or faster. Anything between 2.5 and 4.0 seconds needs improvement, and anything above 4.0 seconds is poor.
INP — Interaction to Next Paint (formerly FID)
In March 2024, Google replaced First Input Delay (FID) with Interaction to Next Paint (INP). INP measures the latency of every tap, click, or keyboard interaction throughout the page lifecycle and reports the worst (or a near-worst) interaction. A good INP is 200 milliseconds or less. Between 200 and 500 ms needs improvement, and above 500 ms is poor. Unlike FID, which only measured the first interaction, INP captures the full interactivity experience.
CLS — Cumulative Layout Shift
CLS quantifies how much visible content shifts around unexpectedly during loading. It is calculated as the sum of layout shift scores for every unexpected movement of visible elements. A good CLS score is 0.1 or less. Between 0.1 and 0.25 needs improvement, and above 0.25 is poor.
Why Google Cares About Core Web Vitals
Google's primary business is connecting users with the best possible answers to their queries. If a page loads slowly, jumps around while rendering, or feels unresponsive when clicked, the user has a poor experience—regardless of how good the content is. Studies have consistently shown that a one-second delay in page load time can reduce conversions by up to 20%. Google validated this correlation by analyzing billions of browsing interactions and confirmed that pages with good Core Web Vitals scores have lower bounce rates and higher user engagement. As of 2025, Core Web Vitals remain a confirmed ranking factor, and Google has hinted they may become more stringent over time. If your competitors have better scores, they will outrank you on pages where the user experience is meaningfully different.
LCP Optimization: Making Your Page Load Instantly
The Largest Contentful Paint is almost always an image, a hero video, or a large heading. Improving LCP requires a multi-pronged approach that targets the entire delivery pipeline from server to screen. Below are five techniques that, combined, will reliably push your LCP under 2.5 seconds.
1. Optimize Images with Modern Formats and Responsive Techniques
Images are the most common LCP element, and they are also the easiest to optimize. Start by serving next-generation formats like WebP and AVIF. WebP typically provides 25–35% smaller file sizes than JPEG or PNG, while AVIF can achieve 50% smaller sizes. Use the <picture> element with type attributes to serve the best format the browser supports:
<picture> <source srcset="hero.avif" type="image/avif" /> <source srcset="hero.webp" type="image/webp" /> <img src="hero.jpg" alt="Hero banner" width="1200" height="600" /> </picture>
Additionally, generate multiple resolutions of each image and use srcset with sizes so mobile users download only what they need. A CDN like Cloudflare, Cloudinary, or Imgix can automate both format conversion and resizing at the edge, dramatically reducing time-to-first-byte and image download time.
2. Eliminate Render-Blocking Resources
Render-blocking CSS and JavaScript delay the browser from painting anything on screen. Inline critical CSS directly into the <head> of your document and defer non-critical styles. For JavaScript, use defer or async attributes to prevent scripts from blocking the parser. Tools like critical (Node.js) can automatically extract above-the-fold CSS for you.
3. Leverage Server-Side Rendering or Static Generation
If you are using a framework like Next.js or Nuxt, choose server-side rendering (SSR) or static site generation (SSG) over client-side rendering (CSR). CSR requires the browser to download, parse, and execute JavaScript before rendering anything meaningful, which is the fastest way to a poor LCP. SSG delivers pre-built HTML instantly and is the gold standard for content-heavy sites. SSR is a strong alternative for dynamic pages, generating HTML on each request at the server rather than in the browser.
4. Preload Critical Assets
The browser discovers resources as it parses the HTML, but you can give it a head start by using <link rel="preload"> for your LCP image, hero font, and critical CSS. This instructs the browser to fetch those resources immediately, before the normal discovery process catches up.
<link rel="preload" href="/hero.webp" as="image" /> <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin />
5. Lazy Loading with next/image (Next.js Example)
If you are using Next.js, the built-in next/image component handles lazy loading, responsive images, and WebP conversion out of the box. Here is how to configure it for an LCP hero image:
import Image from "next/image";
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="Hero banner"
width={1200}
height={600}
priority
sizes="(max-width: 768px) 100vw, 1200px"
/>
);
}The priority attribute tells Next.js to skip lazy loading and preload the image, making it an ideal choice for LCP content. Without priority, Next.js defers image loading by default, which would hurt LCP if applied to the hero image.
INP Optimization: Making Your Site Feel Instant
Interaction to Next Paint measures how quickly your page responds to user input. A high INP is almost always caused by a bloated JavaScript main thread that is too busy to handle events. The solution: ship less JavaScript, break it into smaller chunks, and move heavy work off the main thread.
1. Audit and Shrink Your JavaScript Bundles
Start by running a bundle analyzer (such as webpack-bundle-analyzer or vite-plugin-visualizer) to see exactly what you are shipping. Remove unused libraries, replace heavy frameworks with lighter alternatives, and tree-shake aggressively. A common culprit is moment.js—replace it with date-fns or the native Intl API.
2. Code Splitting and Dynamic Imports
Use dynamic import() to split your JavaScript into route-based or component-based chunks. Frameworks like Next.js and Nuxt support this natively with lazy-loaded components:
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("../components/Chart"), {
loading: () => <p>Loading chart...</p>,
ssr: false,
});
export default function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<HeavyChart />
</div>
);
}This pattern ensures the Chart component and its dependencies are only loaded when the component is actually rendered, keeping the initial JavaScript payload small.
3. Move Heavy Tasks to Web Workers
Web Workers allow you to run JavaScript in a separate thread, keeping the main thread free to handle user interactions. If you have data processing, encryption, or image manipulation tasks, offload them to a worker:
const worker = new Worker(new URL("./dataWorker.js", import.meta.url));
worker.postMessage({ data: rawData });
worker.onmessage = (event) => {
updateUI(event.data);
};4. Minimize Main Thread Work
Break long tasks (tasks over 50 ms) into smaller chunks using setTimeout(), requestIdleCallback(), or scheduler.yield() (if available). The goal is to never block the main thread for more than 50 milliseconds at a time, which is the threshold where users perceive a delay. Also review your third-party scripts—analytics, chat widgets, and ad networks are notorious for consuming main thread time. Load them asynchronously or after the page is fully interactive.
CLS Optimization: Keeping the Page Stable
Cumulative Layout Shift is the most frustrating experience for users: they go to click a button, and the page shifts, making them click something they did not intend. Preventing layout shifts is largely about reserving space before content loads.
1. Always Set Explicit Dimensions on Images and Video
Every <img> and <video> element must have width and height attributes. In modern CSS, combined with max-width: 100% and height: auto, these attributes allow the browser to calculate the aspect ratio and allocate the correct space before the resource loads:
img {
max-width: 100%;
height: auto;
aspect-ratio: attr(width) / attr(height);
}Browsers now automatically derive an aspect ratio from the width and height attributes, so you rarely need to set aspect-ratio explicitly. But it is good to understand the mechanism.
2. Use Aspect Ratio Boxes for Embeds
Embedded content from YouTube, Twitter, or other iframes does not have intrinsic dimensions. The classic solution is the "aspect ratio box" technique using padding-bottom or the CSS aspect-ratio property:
.embed-container {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
}
.embed-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}This ensures the embed container takes up exactly 56.25% (9/16) of its width before the iframe even begins loading.
3. Reserve Space for Dynamic Content
Ads, banners, cookie consent bars, and dynamically injected content are frequent CLS offenders. Always reserve a dedicated container with a fixed height (or min-height) for ads. If the ad fails to load, keep the space reserved or show a placeholder. For banners and notifications that slide in from the top or bottom, use position: fixed or sticky so they are taken out of the normal document flow and do not push content down.
4. Font Loading Strategies to Prevent Layout Shift
Custom fonts often cause CLS because the browser renders text in a fallback font first, then swaps it with the custom font, changing the text dimensions. Use font-display: optional or font-display: swap in your @font-face declaration. For maximum control, preload the font and use the font-size-adjust property to match the fallback font's metrics with your custom font:
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2");
font-display: swap;
size-adjust: 100%;
}Better yet, consider using variable fonts served from a CDN with font-display: optional to eliminate the swap entirely if the font does not arrive in time.
Measuring Core Web Vitals
You cannot improve what you do not measure. Here are the essential tools for monitoring Core Web Vitals in 2025:
Lighthouse (Chrome DevTools)
Run Lighthouse from the "Lighthouse" panel in Chrome DevTools for a lab-based diagnostic report. It provides specific recommendations for each metric, flagged directly in your dev environment.
PageSpeed Insights
Enter any URL at pagespeed.web.dev to get both lab data (from Lighthouse) and field data (from the Chrome User Experience Report, or CrUX). Field data is critical because it reflects real user conditions on various devices and network speeds.
Chrome User Experience Report (CrUX)
CrUX is Google's public dataset of real-user performance data from millions of Chrome users. You can query it via BigQuery or access it through PageSpeed Insights. It is the most authoritative source for your actual Core Web Vitals scores.
Google Search Console
The "Core Web Vitals" report in Search Console surfaces URLs that are "Poor," "Needs Improvement," or "Good" based on CrUX data. It groups issues by metric so you can prioritize which pages to fix first.
Web Vitals Extension
The Web Vitals Chrome Extension overlays real-time LCP, INP, and CLS scores as you browse. It is invaluable for quick A/B testing and verifying fixes during development.
Common Mistakes That Hurt Core Web Vitals
After optimizing dozens of sites, I have noticed the same mistakes appearing over and over. Avoid these at all costs:
- Serving uncompressed images in JPEG/PNG only. WebP and AVIF have been supported in every major browser since 2020 and 2021 respectively. There is no excuse not to use them.
- Loading all JavaScript upfront. Even if you only need a carousel on one page, shipping the entire library site-wide wastes bandwidth and blocks the main thread.
- Setting font-display: swap without size-adjust. The swap can cause a layout shift if the fallback and custom fonts have different metrics. Always pair swap with
size-adjustorascent-override. - Not testing on real mobile devices. Emulated throttling in DevTools is useful but cannot replace testing on an actual Moto G4 or iPhone SE on a 3G connection.
- Ignoring third-party scripts. A single poorly-optimized analytics tag or ad network can add 2–3 seconds to LCP and 100+ ms to INP.
- Fixing metrics in isolation. Compressing an image might improve LCP but add CLS if you remove the dimensions. Always re-run all three metrics after every change.
Setting a Performance Budget
A performance budget is a set of hard limits on metrics or resource sizes that your team agrees not to exceed. Without a budget, performance degrades gradually as new features are added. Here is how to set one:
- Choose your metrics. Stick with the Core Web Vitals thresholds: LCP < 2.5 s, INP < 200 ms, CLS < 0.1.
- Add resource budgets. For example: total JavaScript bundle < 200 kB gzipped, total images < 1 MB, time-to-first-byte (TTFB) < 800 ms.
- Automate enforcement. Use tools like
bundlesize,webpack-performance-budget, or Lighthouse CI to fail builds that exceed budgets. In your CI pipeline, add a step that runs Lighthouse and compares scores against a baseline. - Monitor in production. Set up real-user monitoring (RUM) with tools like SpeedCurve, Datadog RUM, or the open-source
web-vitalslibrary to track scores from actual visitors and alert your team if thresholds are breached.
A performance budget turns performance from an afterthought into a contract. When a developer's PR adds a new library, the CI pipeline will flag it if it pushes the bundle over budget. This prevents the slow creep of bloat that ruins scores over time.
Best Practices Checklist
Use this checklist when auditing any page:
- Images served in WebP/AVIF format with responsive
srcset - LCP image uses
<link rel="preload">orpriorityattribute - Critical CSS inlined, non-critical CSS deferred
- JavaScript loaded with
deferorasync; render-blocking resources eliminated - Dynamic imports used for below-the-fold and interaction-driven components
- Third-party scripts loaded asynchronously or after page become interactive
- All images and iframes have explicit
widthandheightattributes - Embeds use CSS
aspect-ratiocontainer - Ads and banners have reserved space with fixed or min-height
- Custom fonts use
font-display: swaporoptionalwithsize-adjust - Font preloaded with
<link rel="preload"> - Web Workers used for heavy data processing tasks
- Main thread long tasks broken into sub-50 ms chunks
- Core Web Vitals monitored via Search Console, CrUX, and PageSpeed Insights
- Performance budget enforced in CI
- Tested on real mobile hardware with throttled network
Frequently Asked Questions
Are Core Web Vitals still a ranking factor in 2025?
Yes. Google confirmed that Core Web Vitals remain part of the page experience ranking signal. While it is not the heaviest-weighted factor, it can make the difference between two otherwise equal pages.
What is the difference between FID and INP?
FID (First Input Delay) only measured the delay of the very first user interaction. INP (Interaction to Next Paint) measures the latency of all interactions throughout the page lifecycle and reports the worst one. INP provides a more complete picture of interactivity. Google replaced FID with INP in March 2024.
Can I pass Core Web Vitals with a single-page application (SPA)?
Yes, but it is more difficult. SPAs that rely on client-side rendering typically have worse LCP and INP because the browser must download and execute JavaScript before rendering content. Server-side rendering (SSR) or static generation (SSG) is strongly recommended. If you must use CSR, invest heavily in code splitting, lazy loading, and preloading critical resources.
How long do Core Web Vitals take to update after I fix them?
CrUX data is updated on a 28-day rolling window. Once your fixes are live, it can take up to 28 days for Google to collect enough real-user data to reflect the improvement in Search Console or PageSpeed Insights. However, Lighthouse and lab tools will show improvements immediately.
Do Core Web Vitals affect mobile and desktop rankings differently?
Google uses mobile Core Web Vitals for mobile rankings and desktop vitals for desktop rankings. Since most traffic is now mobile, prioritize fixing mobile scores first. Note that the thresholds are the same for both mobile and desktop.
What is a good LCP score for images vs text?
The threshold is the same (under 2.5 seconds) regardless of the element type. However, text-only LCP elements (large headings) are generally easier to optimize than images because they avoid the image download bottleneck. If your LCP is an image, ensure it is preloaded, optimized, and served from a CDN.
Summary & Next Steps
Core Web Vitals are not a one-time fix. They are an ongoing discipline that requires attention throughout development, deployment, and monitoring. By focusing on optimized images, smart JavaScript delivery, and layout stability, you can consistently meet Google's thresholds and deliver a fast, frustration-free experience to your users.
Start by running a PageSpeed Insights report on your most important pages today. Identify your worst metric, apply the corresponding technique from this guide, and re-test. Rinse and repeat until all three metrics are green across your entire site.
If you need hands-on help optimizing your site, check out our website management and updates service or our custom web development offerings. For more foundational guidance, read our SEO best practices guide and our tutorial on how to build a Next.js website. Ready to get started? Contact us or request a quote today.
Explore Maintenance Plans
Keep your website secure, updated, and performing at its best.
About the Author
Zeeshan Waheed
Senior Full Stack Engineer & Web Security Expert with 8+ years of experience. Specializing in Next.js, React, Node.js, cybersecurity, and AI integration.
Get the Latest Insights
Subscribe to receive new articles, tutorials, and updates directly in your inbox.