Web Development

Core Web Vitals in Practice: A Developer's Guide to LCP, INP and CLS

A hands-on guide to Core Web Vitals: what LCP, INP and CLS measure, how to diagnose them, and practical fixes that make your site feel fast, especially on mid-range phones and slow networks.

Benjamin Amoako (ProfJero) · 5 min read

Many of the people who use the websites and apps I build are on mid-range Android phones, sometimes on patchy mobile data. On my laptop with fast Wi-Fi, everything feels instant. On their phones, the same site can feel sluggish, jumpy or unresponsive.

That gap is exactly what Core Web Vitals help you measure. They are Google's set of user-centred performance metrics, and they matter for two reasons: they reflect real user experience, and they are part of how Google evaluates page experience in search.

In this guide, I'll explain the three metrics in plain language and share the fixes that have helped me most.

The three Core Web Vitals

MetricWhat it measures"Good" threshold
LCP (Largest Contentful Paint)How quickly the main content appears2.5 seconds or less
INP (Interaction to Next Paint)How quickly the page responds to user input200 milliseconds or less
CLS (Cumulative Layout Shift)How much the layout jumps around unexpectedly0.1 or less

Google assesses these at the 75th percentile of real page loads — so it is not enough for the site to be fast for some users. It needs to be good for most of them, including those on slower devices.

INP replaced First Input Delay (FID) as a Core Web Vital in 2024. FID only measured the delay before the first interaction began to be processed. INP looks at responsiveness across interactions during the whole visit, which is a much more honest picture — and a harder one to pass.

Lab data vs field data

Before fixing anything, understand the two kinds of data:

  • Lab data comes from tools like Lighthouse in Chrome DevTools. It is a simulated test, great for debugging.
  • Field data comes from real users, for example the Chrome User Experience Report shown in PageSpeed Insights and in Search Console's Core Web Vitals report.

Field data tells you whether you have a problem. Lab data helps you find why. If your site is new or small, you may not have enough field data yet; in that case, measure your own users with the web-vitals JavaScript library:

js
import { onLCP, onINP, onCLS } from "web-vitals";

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    page: location.pathname
  });
  navigator.sendBeacon?.("/analytics", body);
}

onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);

Improving LCP: get the main content on screen fast

The LCP element is usually a hero image, a large heading or a banner. Common problems and fixes:

Make the LCP resource discoverable early

If your hero image is loaded through CSS or JavaScript, the browser discovers it late. Use a normal img tag in the HTML and give it high priority:

html
<img src="/images/hero-800.webp"
     srcset="/images/hero-480.webp 480w, /images/hero-800.webp 800w, /images/hero-1200.webp 1200w"
     sizes="100vw"
     width="1200" height="630"
     fetchpriority="high"
     alt="Students learning to code at a community tech hub">

Never lazy-load the LCP image

loading="lazy" is great for images further down the page, but on the hero image it delays your most important content.

Optimise images

Use modern formats like WebP or AVIF, serve appropriately sized images with srcset, and compress them. Images are often the biggest single win on content sites.

Reduce render-blocking resources

Large CSS files and synchronous scripts in the head delay rendering. Inline small critical CSS, defer non-critical scripts, and remove unused code.

Speed up the server

A slow server response delays everything. Use a CDN, enable caching, and make sure your hosting is close to your users where possible.

Improving INP: make interactions feel instant

INP problems usually come from long tasks on the main thread: JavaScript that runs for a long time and blocks the browser from responding.

Find the slow interactions

In Chrome DevTools, use the Performance panel to record yourself clicking buttons, opening menus and typing in forms. Look for long tasks after each interaction. Test with CPU throttling turned on to simulate a slower phone.

Break up long tasks

If a click handler does a lot of work, update the UI first, then yield to the browser before doing the heavy part:

js
function yieldToMain() {
  return new Promise(resolve => setTimeout(resolve, 0));
}

button.addEventListener("click", async () => {
  button.disabled = true;
  status.textContent = "Saving...";   // visible feedback first

  await yieldToMain();                // let the browser paint

  const report = buildLargeReport(data); // heavy work afterwards
  renderReport(report);
  button.disabled = false;
});

Some browsers also support scheduler.yield() for this purpose; you can feature-detect it and fall back to setTimeout.

Other INP fixes

  • Debounce expensive input handlers, like search-as-you-type, so you don't run heavy work on every keystroke.
  • Reduce DOM size. Rendering thousands of table rows at once is slow; paginate or virtualise long lists.
  • Audit third-party scripts. Chat widgets, trackers and ad scripts can quietly take over the main thread. Keep only what you truly need, and load it late.
  • Move heavy computation to a Web Worker when it doesn't need the DOM.
  • Avoid unnecessary re-renders in frameworks. Profile your components and memoise where it helps.

Improving CLS: stop the page from jumping

Layout shift is the frustration of trying to tap a button just as an image loads and pushes it down.

  • Always set dimensions on images and videos using width and height attributes or CSS aspect-ratio.
  • Reserve space for dynamic content such as ads, embeds and banners.
  • Don't insert content above existing content unless the user asked for it.
  • Be careful with web fonts. Use font-display: swap or optional, preload key fonts, and choose fallback fonts with similar sizing.
  • Animate with transform and opacity, not properties like top or height that trigger layout.

My performance workflow

  1. Check field data in PageSpeed Insights or Search Console.
  2. Identify which metric is failing and on which pages.
  3. Reproduce the issue in DevTools with throttling enabled.
  4. Fix the biggest issue first — usually images for LCP, long tasks for INP, missing dimensions for CLS.
  5. Re-test in the lab, then watch field data over the following weeks.
  6. Add a performance check to your workflow so regressions don't creep back.

Quick checklist

  • Hero image is in the HTML, not lazy-loaded, with fetchpriority="high"
  • Images are compressed, modern formats, properly sized
  • Non-critical JavaScript is deferred
  • Third-party scripts are audited and minimised
  • Long tasks are broken up; interactions give instant visual feedback
  • All images and embeds have reserved space
  • Fonts are loaded without causing big shifts
  • You have tested on a real mid-range phone

Conclusion

Performance is not about chasing a perfect Lighthouse score. It is about respect for your users' time, data and devices. Fixing Core Web Vitals usually makes a site feel calmer, faster and more trustworthy — and that is felt most by the users who have the least powerful phones.

Have you run your site through PageSpeed Insights recently? Share which metric gives you the most trouble in the comments, and I'll try to help.

0 comments 0 views
Benjamin Amoako

Written by Benjamin Amoako (ProfJero)

Full-stack developer and IT trainer from Cape Coast, Ghana. Co-Founder of Digital Bridge Initiative and Founder of ProfJero Hub. I write about building real software and growing in tech.

0 Comments

0/2000