Fixing Slow Pages: A Step-by-Step Core Web Vitals Tutorial

Why Core Web Vitals Actually Matter (Beyond Rankings)

Google's Core Web Vitals aren't just another ranking signal to game. They measure something real: how frustrating your page feels to a real person sitting on a phone with mediocre LTE. If your LCP fires at 5 seconds, that's five seconds of staring at a half-loaded screen. If your layout shifts when an ad pops in, that's a user who just accidentally tapped the wrong thing. These aren't abstract metrics — they're moments where visitors decide to stay or leave.

This guide walks through measuring LCP, CLS, and INP on your own pages and then actually fixing the problems you find. No vague advice like "compress your images." Specific actions, in order.

Step 1: Get Your Baseline Numbers First

Before touching any code, you need real data. There are two kinds: lab data (simulated, from your machine) and field data (real users, real conditions). Both matter for different reasons.

For lab data, open Chrome DevTools, run Lighthouse on your most important page (usually the homepage or a high-traffic landing page), and write down the three scores: LCP, CLS, and INP. Do this in an Incognito window with extensions disabled — they skew results.

For field data, head to Google Search Console and open the Core Web Vitals report. This shows how real visitors experienced your pages over the last 28 days. If you have enough traffic, you'll see pages flagged as "Poor" or "Needs Improvement." This is your actual problem list — prioritize these pages over ones with good field data even if Lighthouse scores look bad on them.

Also bookmark PageSpeed Insights (pagespeed.web.dev). Paste any URL and you get both lab and field data in one view, plus specific opportunities with estimated savings.

Step 2: Fix LCP — The Largest Contentful Paint

LCP measures how long it takes for the largest visible element to render — usually a hero image, a headline, or a large block of text. Good LCP is under 2.5 seconds. Anything above 4 seconds is "Poor."

The single most impactful fix for most sites is this: make your LCP element load earlier. Here's how to identify it and attack the delay.

  1. Find out what your LCP element actually is. In Chrome DevTools, run a Performance recording, then look for the "LCP" marker in the timeline. Or use PageSpeed Insights — it highlights the element in the screenshot.
  2. If it's an image: preload it. Add this in your <head>:
    <link rel="preload" as="image" href="/your-hero-image.webp">
    This tells the browser to start downloading the image before it even parses the CSS or JS that would otherwise discover it late.
  3. Convert images to WebP or AVIF. A JPEG hero image that's 400KB becomes 80–120KB in WebP with no visible quality loss. Use Squoosh.app or run cwebp from the CLI. Then update your <img> tag to use a <picture> element with AVIF as the first source and WebP as fallback.
  4. Check your server's Time to First Byte (TTFB). PageSpeed Insights shows this. If TTFB is above 600ms, your hosting is a bottleneck — a CDN like Cloudflare or a faster server tier will have more impact than any image optimization you do. No amount of frontend work fixes a slow backend.
  5. Remove render-blocking resources. Scripts and stylesheets that load in the <head> without async or defer delay everything. Add defer to non-critical scripts. Inline only the critical CSS needed for above-the-fold content and lazy-load the rest.

Step 3: Fix CLS — Cumulative Layout Shift

CLS is the score that makes users accidentally click things they didn't mean to. It happens when elements move around after the page initially renders. Good CLS is under 0.1. The most common causes are predictable — and fixable.

Images without dimensions are the top offender. When the browser doesn't know how tall an image is before it loads, it allocates no space for it. When the image arrives, everything below it jumps down. Fix this by always specifying width and height attributes on every <img> tag:

<img src="hero.webp" width="1200" height="630" alt="...">

This lets the browser reserve the correct space from the start, even before the image loads. Pair this with height: auto in your CSS and the aspect ratio will be preserved responsively.

Ads, embeds, and iframes are the next major cause. If you're injecting ad slots dynamically, reserve their space with a container that has a fixed min-height. For iframes (YouTube embeds, maps), use the padding-top aspect-ratio trick or the newer CSS aspect-ratio property:

iframe { aspect-ratio: 16/9; width: 100%; }

Web fonts causing FOUT (Flash of Unstyled Text) can also shift layout if the fallback font is significantly wider or taller than the loaded font. Use font-display: swap in your @font-face declarations, and use the size-adjust descriptor to make your fallback font metrics match the web font as closely as possible. The "Fallback font generator" at screenspan.dev helps with this.

Step 4: Fix INP — Interaction to Next Paint

INP replaced FID in March 2024 and it's trickier. It measures the delay between a user interaction (tap, click, keyboard input) and the next visual update. Good INP is under 200ms. Poor INP (above 500ms) means your page feels sluggish when people interact with it.

INP problems are almost always caused by long tasks blocking the main thread. Here's how to find and fix them.

  1. Use the Chrome DevTools Performance panel. Record a session where you click around on your page. Look for tasks longer than 50ms (shown as red triangles on the flame chart). These are your culprits.
  2. Identify what's running during those long tasks. Commonly: third-party scripts (chat widgets, analytics, tag managers), large JavaScript bundles being parsed, or event handlers doing too much work synchronously.
  3. Defer third-party scripts aggressively. Most chat widgets and marketing scripts don't need to load during page startup. Load them after the user's first interaction using a technique like this:
    document.addEventListener('pointerdown', loadThirdPartyScripts, {once: true});
  4. Break up long event handlers. If a click handler does a lot of work (DOM updates, calculations, API calls), it can hold the main thread for hundreds of milliseconds. Use setTimeout(fn, 0) or scheduler.postTask() to yield to the browser between chunks of work, letting it paint before continuing.
  5. Use a web worker for heavy computation. If you're doing data processing or parsing in response to user input, move it off the main thread entirely into a Worker. The main thread stays free to handle user interactions and paint immediately.

Step 5: Verify in the Field, Not Just the Lab

After making your changes, Lighthouse will show immediate improvement — but that's lab data. Field data in Search Console takes 28 days to fully reflect changes because it's a rolling average. Don't panic if you don't see Search Console improve right away.

What you can check immediately: CrUX data via PageSpeed Insights updates faster than Search Console (roughly weekly). Check your URL there and watch the field data section. You can also use the Web Vitals Chrome extension — it shows live LCP, CLS, and INP values as you browse your own pages, so you can validate your fixes feel correct in real usage.

One more thing worth doing: test on a real mid-range Android phone, not just your developer laptop. Chrome DevTools has CPU throttling (4x slowdown mode) that approximates this, but nothing beats actually tapping through your page on a $200 Android device over mobile data. That's the experience the majority of your visitors are having — and it'll show you problems no lab tool will catch.

The Order That Makes the Biggest Difference

If you're overwhelmed by the list, here's the priority sequence that moves the needle fastest for most sites:

  • Fix TTFB first — slow hosting cancels out every other optimization.
  • Preload your LCP image and convert it to WebP/AVIF.
  • Add width/height to all images to kill layout shifts.
  • Audit and defer third-party scripts (this alone often fixes INP dramatically).
  • Reserve space for ads and embeds.

Core Web Vitals aren't a one-time project. Scores degrade as you add features, install new plugins, or integrate new third-party tools. The best practice is to run Lighthouse as part of your deployment pipeline so regressions get caught before they go live — not after they've been hurting real users for weeks.

Disclaimer: This article is for general informational and educational purposes only and does not constitute professional, financial, medical, or legal advice. Results from any tool are estimates based on the inputs provided. Always verify important details and consult a qualified professional before making decisions.