Skip to main content
Core Web Vitals Tuning

The Cumulative Layout Shift Culprit You're Overlooking: How Asynchronous Ad Loading Undermines Your CLS

You've optimized images, preloaded fonts, and set width and height on every <img> tag. Your Lighthouse CLS score is green in lab tests. Yet in the field—real users on slow connections—your Cumulative Layout Shift is still red. The culprit is often hiding in plain sight: asynchronous ad loading. We see teams spend weeks on font-display and image dimensions, only to let ad scripts shift the entire page layout after the user has started reading. This guide explains how async ads break CLS, what patterns actually work, and where most implementations go wrong. 1. Where the Layout Shift Actually Happens Ad-related layout shift doesn't occur when the ad script loads—it happens later, when the ad creative renders and the browser finally knows the ad's true dimensions. Asynchronous loading delays this render, but it doesn't eliminate the shift.

You've optimized images, preloaded fonts, and set width and height on every <img> tag. Your Lighthouse CLS score is green in lab tests. Yet in the field—real users on slow connections—your Cumulative Layout Shift is still red. The culprit is often hiding in plain sight: asynchronous ad loading. We see teams spend weeks on font-display and image dimensions, only to let ad scripts shift the entire page layout after the user has started reading. This guide explains how async ads break CLS, what patterns actually work, and where most implementations go wrong.

1. Where the Layout Shift Actually Happens

Ad-related layout shift doesn't occur when the ad script loads—it happens later, when the ad creative renders and the browser finally knows the ad's true dimensions. Asynchronous loading delays this render, but it doesn't eliminate the shift. In a typical scenario, the page loads, the ad slot is initially empty or shows a tiny placeholder, and then seconds later a 300x250 banner pops in, pushing content down. The shift is measured by the browser as a layout shift score, and if the ad appears near the viewport center, the impact is multiplied by the distance moved.

We often see this with sticky headers or sidebars that resize after ads load. One common pattern: a site uses an async ad script that inserts a <div> with zero height, then the ad network fills it after a bid request. The browser has already painted the page, so when the ad renders, everything below it shifts. The CLS score can spike by 0.2 or more per ad unit.

Why async doesn't equal stable

Async loading means the script doesn't block the page render, but it doesn't guarantee the ad slot has a defined size. Many ad networks serve creatives of varying dimensions based on the user's device, viewport, or history. Without a fixed container, the browser cannot reserve space accurately. The result is a layout shift that happens after the user has started interacting—exactly the kind Google's CLS metric penalizes most.

Field data vs. lab data

Lighthouse runs in a controlled environment with no real ad bids, so it often reports a perfect CLS. Real-world Chrome User Experience Report (CrUX) data tells a different story. We've seen sites with a Lighthouse CLS of 0.02 but a field CLS of 0.35. The difference is almost always third-party content: ads, embeds, or widgets. Async loading masks the problem in development but doesn't fix it in production.

One team we read about had a site with three ad slots: one in the header, one in the sidebar, and one in the content. They loaded all ads asynchronously. In lab tests, everything looked fine. In the field, the header ad often loaded late, shifting the entire page down by 90 pixels. The sidebar ad sometimes rendered as a tall skyscraper, pushing the main content column to the right. The content ad would appear after the user had scrolled past it, causing a shift in the middle of an article. Each shift was small individually, but together they pushed the 75th percentile CLS to 0.4.

2. Foundations Readers Confuse

There are three common misconceptions about async ads and CLS. First, many believe that if the ad script is asynchronous, the layout shift is somehow less harmful. That's false—the shift still occurs, and because it happens later, it's more likely to disrupt the user. Second, some think that setting a fixed width and height on the ad container via CSS solves everything. But if the ad network serves a creative larger than the container, the container may overflow or the ad may be clipped, causing layout shifts in neighboring elements. Third, people assume that lazy-loading ads (loading them only when they enter the viewport) is safe for CLS. In reality, lazy-loaded ads can shift content that the user is about to read, creating a jarring experience.

CSS aspect-ratio boxes are not enough

A popular fix is to wrap each ad slot in a <div> with an aspect-ratio property, hoping the browser reserves space proportionally. This works if the ad always fills the box exactly. But many ad networks serve responsive creatives that change size based on viewport width. If the aspect ratio doesn't match the creative, the browser may still shift. For example, a 16:9 container might get a 4:3 creative, causing black bars or overflow. The layout doesn't shift in the traditional sense, but the visual experience is broken, and users may see a flash of empty space.

Placeholder sizing is a guessing game

Some sites use a placeholder element with a fixed height, like min-height: 250px. This prevents the layout from collapsing, but if the ad creative is taller than the placeholder, the page shifts again. If the creative is shorter, there's wasted space. Ad networks often change their creative sizes without notice, so the placeholder becomes outdated. We've seen cases where a site used a 300x250 placeholder for years, and then the ad partner introduced a 300x600 unit for certain campaigns, causing massive shifts.

Another confusion is around the loading='lazy' attribute on iframes. Some developers assume that lazy-loading an ad iframe will prevent layout shift because the iframe doesn't load until near the viewport. But the shift still happens when the iframe content renders—it's just delayed. The browser already reserved space (or didn't), so the shift is the same. Lazy loading only changes the timing, not the magnitude.

3. Patterns That Usually Work

After auditing dozens of ad implementations, we've found three patterns that reliably keep CLS under 0.1 even with async ads. The first is explicit container sizing with a fallback. Instead of relying on the ad network to report dimensions, you set a fixed width and height on the ad container using CSS, and you also set overflow: hidden to prevent the ad from breaking out. This ensures that no matter what creative is served, the container stays the same size. The downside is that you may clip part of the ad, but many networks support responsive creatives that fit within the box.

Use a dedicated ad server with size mapping

If you control the ad server (like Google Ad Manager), you can define size mappings that tell the ad to only serve creatives that fit your container. For example, you can create a mapping that allows only 300x250, 300x600, and 320x50. The ad server will then select a creative that matches one of those sizes. The container can be sized to the largest expected size, and smaller creatives are centered or aligned. This eliminates the guesswork.

Implement a layout shift observer

We recommend adding a PerformanceObserver in JavaScript to monitor layout shifts in real time. If an ad causes a shift, you can log the details and even adjust the container dynamically. For example, if you detect that an ad slot shifted the page by more than 50 pixels, you could collapse the slot entirely and show a fallback (like a static image). This is an advanced pattern but gives you a safety net.

Test with real ad creatives

Many teams test ads using placeholder images or empty containers. That hides the problem. Instead, use actual ad creatives from your network, including the largest and smallest sizes. Simulate slow networks (e.g., 3G throttling) to see when ads load and how they affect the layout. We've found that ads that load after the user starts scrolling are particularly harmful, so consider delaying non-critical ad slots until the page is fully stable.

4. Anti-Patterns and Why Teams Revert

The most common anti-pattern is assuming that async loading alone solves CLS. Teams often add async or defer to the ad script and call it done. They don't reserve space, so the ad slot is zero-height until the creative arrives. The shift is inevitable. Another anti-pattern is using JavaScript to insert ad containers dynamically after the page load. This guarantees a layout shift because the DOM is modified after paint.

Relying on ad network's built-in placeholders

Some ad networks offer a placeholder that shows a gray box or a loading spinner. These placeholders often have a fixed size, but if the network changes the placeholder size or the creative overflows, the layout shifts. We've seen networks update their placeholder code without warning, breaking sites that depended on it. The safest approach is to define your own container with your own CSS, not the network's.

Using CSS animations to hide shifts

A desperate tactic is to animate the ad container's height from 0 to its final size, hoping the transition will be smooth. But CSS transitions do not prevent layout shift—the browser still measures the shift, and the animation only changes the visual appearance. The layout shift score is calculated based on the final position, not the animation. So this trick doesn't help your CLS metric.

Teams revert to these anti-patterns because they are easy to implement and seem to work in quick tests. But over time, as ad networks evolve and page content changes, the layout shifts return. We've seen sites that were stable for months suddenly get a CLS penalty after an ad network update. The only durable solution is to own the container sizing yourself.

5. Maintenance, Drift, or Long-Term Costs

Even with a good initial setup, ad containers drift over time. Ad networks introduce new creative sizes, change their responsive behavior, or update their JavaScript. Your carefully sized containers may no longer match. We recommend quarterly audits: check each ad slot's actual rendered size in the field using RUM (Real User Monitoring) data. If you see shifts, update the container CSS or the size mapping.

Cost of over-reserving space

If you set containers too large to accommodate any possible creative, you waste valuable viewport space. This can reduce ad revenue because smaller ads may not fill the space, or because the page becomes longer, reducing user engagement. There's a trade-off between CLS stability and revenue. Some teams accept a slight CLS increase (e.g., 0.05) to maximize ad fill rate. That's a business decision, but it should be intentional, not accidental.

Ad blocker interference

When ad blockers are active, the ad container may remain empty. If you haven't set a fixed height, the container collapses, causing a layout shift. Some ad blockers also inject their own styles, which can break your container. A common solution is to use a placeholder that is always visible (like a colored box) and replace it with the ad if it loads. If the ad is blocked, the placeholder remains, preserving the layout.

6. When Not to Use This Approach

Async ad loading with fixed containers is not always the best choice. If your ad network does not support responsive sizing or size mapping, you may have to accept some shifts. For example, some smaller ad networks serve only one creative size and cannot adapt. In that case, you can set the container to that exact size and hope the network doesn't change it. But if the network changes, you'll need to update manually.

When you need above-the-fold revenue

If your business depends on ads appearing above the fold immediately, delaying them with async loading may hurt revenue. Some advertisers pay a premium for fast-loading ads. In that case, you might choose to load a critical ad synchronously (with a fixed container) and accept the potential render-blocking cost. This is a trade-off between CLS and revenue that only your team can evaluate.

When using header bidding

Share this article:

Comments (0)

No comments yet. Be the first to comment!