Performance
INP, LCP, CLS: How to Improve Core Web Vitals Step-by-Step
Concrete steps to improve each Core Web Vital — measurement, fix order, the images/fonts/scripts that cause most failures, and performance budgets that stop regressions coming back.

Core Web Vitals (LCP, INP, CLS) are the metrics that matter for UX and search. This guide walks you through improving each one step by step: how to measure, what to fix first, and how to re-check so your site stays in the green. Whether you're new to performance or tuning an existing app, you'll have a clear order of operations.
Why Core Web Vitals and in what order
LCP (Largest Contentful Paint) affects perceived load speed; INP (Interaction to Next Paint) affects how responsive the page feels; CLS (Cumulative Layout Shift) affects visual stability. Search engines use these (and related metrics) as signals. Fix LCP first (biggest perceived impact), then CLS (often quick wins), then INP (often requires more code and measurement). After each change, re-run Lighthouse or check CrUX so you see the effect and don't regress.
LCP: get the largest element fast
LCP is the load time of the largest visible content (usually an image or text block above the fold).
- Step 1: Identify the LCP element (Lighthouse "Largest Contentful Paint" or DevTools Performance).
- Step 2: Make sure it's in the initial HTML and not blocked—avoid loading it only after JS runs. If it's an image, don't lazy-load it above the fold.
- Step 3: Optimize the resource: resize the image to display size, serve WebP/AVIF, use priority or preload so the browser fetches it early. For Next.js, use
priorityon the LCP image. - Step 4: Improve server response (TTFB) with caching or a faster host so the HTML and LCP resource start quickly.
Re-measure; LCP should drop into the "good" range (under about 2.5s).
Beginner tip: In Lighthouse, click the LCP element in the timeline to see what it is. Often it's the hero image or main heading—optimize that first.
INP: keep interactions responsive
INP measures responsiveness to clicks and taps (how long until the next paint after input).
- Step 1: Find long tasks and heavy handlers in the Performance panel or with Lighthouse.
- Step 2: Reduce JS: code-split and lazy-load below-the-fold or non-critical code; defer third-party scripts (analytics, chat) until after load or on interaction.
- Step 3: Break up long work: move heavy logic to a worker or chunk it (e.g.
requestIdleCallback, or yielding in loops) so the main thread stays free. - Step 4: Optimize event handlers: keep them short; defer non-critical updates or use passive listeners where possible.
Re-test; INP should stay low so interactions feel instant.
Expert tip: Use Chrome's "Long tasks" and "Main thread" view to see what blocks input. Often the culprit is a large bundle or a single heavy handler—fix that before micro-optimizing.
CLS: prevent layout shift
CLS is the cumulative layout shift from unexpected resizing or movement.
- Step 1: Find shifting elements in Lighthouse (Experience → Cumulative Layout Shift) or the layout shift regions in DevTools.
- Step 2: Images: always set width and height (or aspect-ratio) so space is reserved before the image loads. Use
aspect-ratioor explicit dimensions in CSS or on<img>/ Next.jsImage. - Step 3: Fonts: use font-display and match fallback metrics (size, line-height) so text doesn't jump when the custom font loads.
- Step 4: Dynamic content (ads, embeds): reserve space with a min-height or aspect-ratio container so the layout doesn't jump when they load.
Re-run; CLS should be under about 0.1.
The three assets behind most failures
Across audits, the same three resource types account for the majority of failing vitals. It is worth attacking them directly rather than metric by metric.
Images
Images are usually both the LCP element and the largest CLS contributor.
- Always give intrinsic dimensions.
widthandheighton<img>, or anaspect-ratiocontainer. This is what reserves the space that prevents the shift. - Size to display, not to source. A 4000px photo rendered at 800px wastes most of its bytes. Use
sizesso the browser picks sensibly across breakpoints. - Serve WebP or AVIF. Typically 25–50% smaller than JPEG at equivalent quality.
priorityon the LCP image, lazy everywhere else. In Next.js,priorityemits a preload and disables lazy loading. Putting it on more than one or two images defeats the point — you are telling the browser everything is urgent.
<Image
src="/hero.jpg"
alt="..."
width={1200}
height={630}
priority
sizes="(max-width: 768px) 100vw, 1200px"
/>A blur placeholder is worth adding for large images: it does not change LCP, but it removes the blank gap that makes a page feel slower than it measures.
Fonts
font-display: swapso text paints in a fallback immediately rather than waiting.- Self-host and preload the one or two faces used above the fold. A webfont fetched from a third-party origin adds a DNS lookup and a connection before a single glyph arrives.
- Match fallback metrics. Swapping from a fallback with different metrics is itself a layout shift.
size-adjustandascent-override— ornext/font, which computes them for you — keep the swap invisible. - Subset. Latin-only is a fraction of the size of a full multilingual face.
Third-party scripts
Analytics, chat widgets, tag managers and embeds are the usual reason a fast site feels slow, because they compete for the main thread exactly when the user is trying to interact.
- Defer everything non-critical. In Next.js,
next/scriptwithstrategy="afterInteractive", orlazyOnloadfor anything not needed at all until idle. - Load chat widgets on interaction, not on page load. A button that mounts the widget on click costs nothing until someone wants it.
- Audit periodically. Third-party scripts accumulate; nobody removes the tag from a campaign that ended.
Performance budgets, so it stays fixed
Fixing vitals once is easy. Keeping them fixed while a team ships weekly is the actual problem, and it is what budgets solve.
Budget the things that cause regressions rather than the metrics themselves:
| Budget | A reasonable starting point |
|---|---|
| JS transferred (initial route) | 170KB compressed |
| CSS | 60KB compressed |
| Largest image | 200KB |
| Total requests before interactive | 50 |
| LCP (field) | 2.5s |
| CLS (field) | 0.1 |
Set the numbers from your current measurements, not from an ideal — a budget you already fail is one everyone learns to ignore. Take today's figure, hold the line, and ratchet down as you improve.
Then enforce it in CI. Lighthouse CI on pull requests, or a bundle-size check that fails the build when a route exceeds its allowance, turns performance from a periodic cleanup into a thing that cannot regress silently. The value is not the number; it is that someone has to consciously decide to exceed it.
A quick audit, in order
When you inherit a site and need a read in ten minutes:
- Run Lighthouse on mobile, throttled. Desktop numbers flatter and mislead.
- Identify the LCP element in the trace. If it is an image, check whether it is lazy-loaded or waiting on JS.
- Sort network by size. The largest one or two resources usually explain the load time on their own.
- Check the main thread for long tasks. Anything over 50ms is a candidate; anything over 200ms is the problem.
- Look for layout shift regions in DevTools rendering. Almost always an image without dimensions or a font swap.
- Compare against field data in Search Console or CrUX. Lab data is a diagnostic; field data is the truth, and they routinely disagree.
Summary
- LCP: Identify the LCP element, keep it in initial HTML, optimize size/format/priority, improve TTFB.
- INP: Reduce and chunk JS, keep handlers light, defer third-party scripts.
- CLS: Reserve space for images (width/height or aspect-ratio), fonts (fallbacks), and dynamic content.
- Assets: Images sized and prioritised, fonts self-hosted with matched fallback metrics, third-party scripts deferred or loaded on interaction.
- Budgets: Set from today's numbers, enforced in CI, so fixes do not quietly erode.
Fix in that order, re-measure against field data, and hold the line with a budget.
Related: website speed audits as a repeatable process and caching strategy across browser, CDN and server.
Related reading
- WordPressWordPress Performance: Caching, Images, and Core Web VitalsThe three levers that move WordPress performance the most — page and object caching, image delivery, and script load order — in the order we actually fix them on client sites, with the tradeoffs at each step.
- PerformanceWebsite Speed Audits: A Repeatable Process That WorksA fixed process for running speed audits that actually produce comparable results over time — the test-set discipline, the lab-vs-field data distinction, and how to prioritize fixes that move the needle.
- PerformanceCaching Strategy 101: Browser, CDN, and Server Explained SimplyHow browser, CDN, and server caches actually interact — the specific headers and revalidation patterns that get you speed without serving stale content where it costs you.
More in WordPress