Two of Google’s three Core Web Vitals — Cumulative Layout Shift (CLS) and Interaction to Next Paint (INP) — are frequently misunderstood, yet both directly impact your search rankings and are flagged regularly in Google Search Console.
This guide covers both metrics: what they measure, how to diagnose failures, and how to fix them on WordPress, Shopify, or any CMS.
Already working on your LCP? See our LCP fix guide for the third Core Web Vital.
Part 1: Cumulative Layout Shift (CLS)
What Is CLS?
CLS measures how much your page’s content unexpectedly moves while loading. Have you ever tried to click a button on a mobile page, only to have an image load above it and push the button down — so you accidentally tap something else? That is layout shift, and Google measures it.
Google’s [2026] CLS targets:
- Good: Under 0.1
- Needs improvement: 0.1 – 0.25
- Poor: Over 0.25
Poor CLS is both a ranking penalty and a user experience disaster — it causes mis-taps, accidental purchases, and frustrated users who leave.
How to Find Your CLS Score
In Google Search Console:
- Go to GSC → Core Web Vitals → Mobile report
- Click into “Poor” pages — GSC will show you the specific CLS issue type
- Common issue labels: “Layout shift from unsized media”, “Layout shift from web fonts”, “Layout shift from dynamically injected content”
In Chrome DevTools:
- Open your page in Chrome → press F12 → go to the Performance tab
- Click record, let the page load, then stop recording
- In the Experience section, layout shift events appear as red blocks
- Click each block to see which element caused the shift and where it came from
In PageSpeed Insights:
- The CLS value appears under Lab Data
- The Diagnostics section lists which specific elements are causing layout shift
The 5 Most Common CLS Causes and Fixes
Cause 1: Images and Videos Without Defined Dimensions
The most common cause of CLS. When the browser starts loading your HTML, it does not know how large an image will be until the image file itself starts downloading. Without pre-defined dimensions, the browser assigns the image zero height — then suddenly jumps the entire page layout down when the image loads.
Fix: Always define width and height attributes on every <img> tag:
<!-- WRONG — browser does not know the height until image loads -->
<img src="product.webp" alt="Product">
<!-- RIGHT — browser reserves the correct space immediately -->
<img src="product.webp" width="800" height="600" alt="Product>
For responsive images, also add CSS:
img {
max-width: 100%;
height: auto;
}
This is the single highest-impact CLS fix for most websites and takes minutes to implement.
For WordPress: Install the “Specify Image Dimensions” feature in your caching or optimisation plugin. WP Rocket handles this automatically. For individual images, ensure you set dimensions in the media editor.
Cause 2: Ads, Embeds, and Iframes Without Reserved Space
Advertisements, social media embeds, and iframes often have unknown dimensions until they load. When they appear mid-page, they push all content below them downward — causing significant CLS.
Fix: Reserve space for ads and embeds using a CSS wrapper with a fixed aspect ratio:
.ad-container {
width: 100%;
min-height: 250px; /* Reserve space for the ad */
}
For Google AdSense, use the “Auto ads” format’s size hints feature to tell the browser the ad dimensions before loading.
For social media embeds (Twitter/X, Instagram, YouTube), use aspect-ratio CSS:
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
Cause 3: Dynamically Injected Content Above Existing Content
If JavaScript inserts content (a banner, cookie consent bar, promotional notice, chat widget, newsletter popup) above already-visible content after the page has loaded, everything below shifts down.
Fix:
- Cookie consent bars: Use a consent manager that reserves space for the bar in the initial HTML rather than injecting it after load. Cookiebot and GDPR Cookie Consent both have CLS-safe implementations.
- Promotional banners: Ensure banners are part of the initial HTML, not JavaScript-injected. If they must be JS-injected, insert them below the fold.
- Chat widgets: Most chat widget scripts insert a fixed-position button that does not cause CLS. If yours does cause shift, move the chat widget script to load after the page is fully interactive.
Cause 4: Web Fonts Causing FOUT (Flash of Unstyled Text)
When a custom font loads and replaces a fallback system font, the different glyph widths can cause text to reflow — pushing other content elements around on the page.
Fix:
- Add font-display: optional or font-display: swap to font declarations (swap is slightly more CLS-prone than optional, but both are better than the default)
- Use the size-adjust CSS property to make your fallback font match the dimensions of your web font more closely — reducing the visual shift when the real font loads
-
Preload critical fonts so they load before the first render:
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>;
Cause 5: Animations That Move Existing Content
CSS or JavaScript animations that move page elements (sliding content in from the side, expanding accordions, animated headers) contribute to CLS if they affect elements that push other content.
Fix: Use CSS transform and opacity for animations instead of properties that affect layout (margin, padding, height, top, left). Transforms are GPU-accelerated and do not trigger layout recalculation:
/* CAUSES CLS — changes layout */
.animate { margin-top: 0; animation: slideDown 0.3s; }
@keyframes slideDown { from { margin-top: -50px; } to { margin-top: 0; } }
/* NO CLS — transform does not affect layout */
.animate { transform: translateY(0); animation: slideDown 0.3s; }
@keyframes slideDown { from { transform: translateY(-50px); } to { transform: translateY(0); } }
Part 2: Interaction to Next Paint (INP)
What Is INP?
INP replaced FID (First Input Delay) as a Core Web Vital in March 2024. While FID measured only the delay before a browser starts responding to the very first user interaction, INP measures the full duration of every interaction throughout the page lifecycle — from when a user taps, clicks, or types, to when the next frame is painted.
If your page is slow to respond to button clicks, form inputs, dropdown selections, or search queries — even after the page has fully loaded — you will have a poor INP score.
Google’s [2026] INP targets:
- Good: Under 200 milliseconds
- Needs improvement: 200 – 500 milliseconds
- Poor: Over 500 milliseconds
Poor INP is particularly common on JavaScript-heavy pages and ecommerce stores with complex product filtering.
How to Find Your INP Score
In Google Search Console:
- GSC → Core Web Vitals → Check for “Poor INP” entries
- Note: INP data in GSC is collected from real users over 28 days. It may take weeks for fixes to show in GSC reports.
In Chrome DevTools:
- Open DevTools → Performance tab → Enable “Web Vitals” checkbox
- Interact with your page (click buttons, type in search boxes, open menus)
- The Performance trace will show INP candidates highlighted
- Look for long “Interaction” blocks
In PageSpeed Insights (field data):
- Scroll to “Core Web Vitals Assessment” — if INP appears, it reflects real-user data from the Chrome User Experience Report
The 5 Most Common INP Causes and Fixes
Cause 1: Long JavaScript Tasks Blocking the Main Thread
The browser’s main thread handles everything: parsing HTML, running JavaScript, responding to user interactions, and painting pixels to screen. If a long JavaScript task (over 50ms) is running when a user clicks, the browser cannot respond to the click until that task completes.
How to identify: In DevTools Performance trace, look for red-flagged “Long Tasks” — any JavaScript task over 50ms. These block user interactions.
Fix:
- Break long JavaScript tasks into smaller chunks using setTimeout() or requestIdleCallback() — allowing the browser to process user interactions between chunks
- Defer non-critical JavaScript to load after the page is interactive
- Remove or delay third-party scripts that run long tasks (analytics, ad scripts, tag managers loading many tags)
For WordPress: WP Rocket’s “Delay JavaScript Execution” setting prevents third-party scripts from running until the user first interacts with the page — significantly reducing INP.
Cause 2: Unnecessary Re-renders From JavaScript Frameworks
Sites built with React, Vue, Angular, or similar JavaScript frameworks can suffer poor INP if component updates trigger large re-renders of the DOM. A user clicking a button that triggers a state change should only update the specific elements that need to change — not re-render entire sections of the page.
Fix:
- Use React’s useMemo and useCallback hooks to prevent unnecessary re-renders
- Virtualise long lists (React Window, TanStack Virtual) so the DOM only contains visible items
- Split large React components into smaller ones so updates are localised
Cause 3: Complex Event Handlers Running Heavy Processing
If your page has event listeners that perform expensive operations synchronously on user interactions (searching a large dataset, complex calculations, DOM manipulations), the browser freezes until the operation completes.
Fix:
- Move expensive processing to a Web Worker — a background thread that does not block the main thread
-
Debounce rapid-fire events like keyup in search boxes:
let timeout; searchInput.addEventListener('keyup', () => { clearTimeout(timeout); timeout = setTimeout(() => runSearch(), 300); }); - Virtualise or paginate results — do not process 5,000 items when 20 are visible
Cause 4: Excessive DOM Size
Pages with thousands of DOM nodes (complex WordPress pages built with drag-and-drop builders, ecommerce pages with large product grids) are slower to update because every style change, layout calculation, and paint operation must account for more elements.
Fix:
- Target a maximum of 1,500 DOM nodes per page (check in DevTools → Elements tab)
- Remove unnecessary wrapper divs introduced by page builders
- Use CSS content-visibility: auto on off-screen sections to skip rendering them until they scroll into view
Cause 5: Stylesheets Not Optimised for Interaction
Large CSS files with complex selectors can slow down style recalculation after user interactions — adding to INP delay.
Fix:
- Avoid universal selectors (*) and overly specific selectors in interactive components
- Use CSS custom properties (variables) for theme values — they update faster than JavaScript-driven style changes
- Remove unused CSS (PurgeCSS for Tailwind users, UnCSS for others)
CLS and INP Fix Priority Order
Work through fixes in this order for maximum impact with minimum effort:
- Add image dimensions to all <img> tags (CLS — takes 1–2 hours, huge impact)
- Reserve ad/embed space with CSS min-height (CLS — 30 minutes)
- Delay third-party JavaScript with WP Rocket or similar (INP — 15 minutes)
- Fix font loading with font-display: swap + preload (CLS — 30 minutes)
- Remove unused JavaScript (INP — ongoing)
- Fix animation properties to use transform/opacity (CLS — 1–2 hours)
- Break up long tasks in custom JavaScript (INP — complex, developer work)
When to Get Professional Help
CLS is generally fixable by following the steps above. INP can be more complex — especially if poor INP is caused by framework-specific re-rendering issues or custom JavaScript — and may require a developer with performance optimisation experience.
If your Core Web Vitals remain in the “Poor” range after implementing the above fixes, our WordPress speed optimisation service includes a full CWV audit and implementation. Get a quote here.



