DEV Community

Cover image for Phantom CLS in Lighthouse: How to Hunt Down and Fix Invisible Layout Shifts
Raj
Raj

Posted on • Originally published at rajshukla-portfolio.vercel.app

Phantom CLS in Lighthouse: How to Hunt Down and Fix Invisible Layout Shifts

Phantom CLS in Lighthouse: How to Hunt Down and Fix Invisible Layout Shifts

Published on June 29, 2026 · 5 min read

By Raj Pawan Shukla


You run Google Lighthouse expecting a great performance score.

Everything looks visually stable.

Yet Lighthouse reports:

"Avoid large layout shifts."

You reload the page several times, carefully watching every element. Nothing appears to move.

So what's causing the poor Cumulative Layout Shift (CLS) score?

This is what I like to call Phantom CLS—layout shifts that happen so quickly they're almost impossible to notice with the naked eye, yet Chrome and Lighthouse still detect them.

While optimizing the Newport Fasteners ecommerce platform, I encountered exactly this problem. Here's how I tracked it down and fixed it.


What Causes Phantom CLS?

The two most common culprits are:

  • Lazy-loaded images above the fold – Images that should be visible immediately are unnecessarily deferred.
  • Client-side injected UI components – JavaScript renders elements slightly after the initial HTML has already been painted, causing nearby content to shift.

Both of these appeared during my investigation.


Case 1: The Lazy-Loaded Hero Banner

The homepage featured a large promotional banner that was being lazy-loaded.

On a fast internet connection, the image appeared almost instantly, so the layout shift was practically impossible to notice. However, Lighthouse consistently flagged it as a contributor to CLS.

The underlying issue was simple:

The browser didn't know how much space to reserve before the image loaded. When the image finally appeared, it pushed surrounding content just enough for Lighthouse to record a layout shift.

The Fix

For images that appear above the fold:

  • Remove loading="lazy"
  • Add fetchpriority="high"
  • Preload the image
  • Always specify explicit width and height

Before

<img src="hero-banner.jpg" loading="lazy" alt="Promo">
Enter fullscreen mode Exit fullscreen mode

After

<link rel="preload" as="image" href="hero-banner.jpg">

<img
    src="hero-banner.jpg"
    fetchpriority="high"
    width="1200"
    height="400"
    alt="Promo"
/>
Enter fullscreen mode Exit fullscreen mode

After these changes, the layout shift for the hero banner disappeared completely.


Case 2: Render-Blocking UI Scripts

The product pages still showed tiny layout shifts during the initial render.

Using Chrome DevTools, I traced them to two JavaScript files:

  • df_add_to_cart_hyva.min.js
  • nouislider.min.js

These scripts initialized the Add to Cart section and the price range slider shortly after the browser painted the initial HTML.

Although the delay lasted only a fraction of a second, the components expanded after JavaScript execution, causing nearby elements to move.

The movement was almost invisible to users—but Lighthouse still recorded it.

The Fix

The solution required two changes together.

1. Reserve Layout Space

Even before JavaScript executes, reserve enough space for the components.

.price-slider-container {
    min-height: 60px;
}

.add-to-cart-wrapper {
    min-height: 48px;
}
Enter fullscreen mode Exit fullscreen mode

2. Defer Non-Critical JavaScript

Instead of blocking HTML parsing, allow the browser to continue rendering while downloading JavaScript.

Before

<script src="df_add_to_cart_hyva.min.js"></script>
<script src="nouislider.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

After

<script src="df_add_to_cart_hyva.min.js" defer></script>
<script src="nouislider.min.js" defer></script>
Enter fullscreen mode Exit fullscreen mode

Important: Using defer alone does not eliminate CLS. In fact, it can make layout gaps more noticeable if no space has been reserved. Stable layouts require both reserved space and deferred script loading.


How to Debug Phantom CLS

Don't trust your eyes—trust the tools.

1. Chrome DevTools Performance Panel

Record a page load and inspect the Experience track.

Chrome highlights exactly:

  • Which DOM elements shifted
  • When the shift occurred
  • How much they contributed to CLS

This is the fastest way to locate invisible layout shifts.


2. Web Vitals Extension

Install the Web Vitals browser extension to log layout shifts in real time while interacting with your website.

It makes identifying problematic components much easier during development.


3. Test on Throttled Connections

Phantom CLS often hides behind fast hardware and local development environments.

Switch Chrome DevTools to Fast 3G or Slow 4G.

Many layout shifts that are invisible on broadband become immediately obvious under realistic network conditions.


The Takeaway

Core Web Vitals measure real user experience—not just what your page looks like after everything finishes loading.

Even movements that last only a few milliseconds can make a page feel unstable and negatively impact your Lighthouse score.

Fixing Phantom CLS requires changing how you think about rendering.

Instead of focusing only on the final appearance of a page, think about the browser's journey from the very first paint to the fully interactive state.

A few simple practices can prevent most layout shifts:

  • Reserve space before dynamic content loads.
  • Avoid lazy-loading above-the-fold images.
  • Prioritize hero assets using fetchpriority and preload.
  • Defer non-critical JavaScript.
  • Always verify improvements using Chrome DevTools and Lighthouse.

Performance optimization isn't always about making pages faster—it's also about making them feel stable.

Sometimes the hardest performance bugs are the ones users never consciously notice.


Have you encountered Phantom CLS?

I'd love to hear about the trickiest layout shift issues you've debugged. Share your experience in the comments, and let's learn from each other's performance wins and challenges.

Top comments (0)