Skip to content

Lazy loading ad slots without losing revenue

Short answer

Request the ad before the slot reaches the viewport, not when it arrives. Use an IntersectionObserver with a root margin large enough to cover the auction plus rendering — commonly 200 to 600 pixels, more on fast scroll. Load above-the-fold slots immediately and never lazy-load your LCP-adjacent placement. Measured impressions will drop; viewability and revenue per impression should rise.

Last updated Intermediate

Lazy loading ads is a rare optimisation that improves speed and yield at the same time — provided the timing is right. Get it wrong in either direction and you lose on both.

The timing problem

An ad needs the auction (up to your timeout) plus the ad server decision plus creative rendering. That is easily a second, sometimes two. If you start when the slot enters the viewport, the user is looking at an empty box for that whole time, and if they keep scrolling, the ad renders behind them and never becomes viewable.

So the trigger has to fire early enough that the ad is ready when the slot arrives. That is what root margin is for.

// Fire when the slot is still 400px below the viewport.
var observer = new IntersectionObserver(function (entries) {
  entries.forEach(function (entry) {
    if (!entry.isIntersecting) return;
    observer.unobserve(entry.target);
    requestAd(entry.target);   // auction, then ad server
  });
}, { rootMargin: '400px 0px' });

document.querySelectorAll('[data-ad-slot]').forEach(function (el) {
  observer.observe(el);
});

Choosing the margin

  • Too small and ads arrive late: low viewability, visible empty boxes, users scrolling past unfilled space.
  • Too large and you are effectively not lazy loading: you request ads for slots plenty of users never reach, and you get the latency cost without the benefit.
  • 200–600 px suits most layouts. Mobile wants the higher end, because scroll velocity relative to viewport height is much greater.
  • Better still, scale it with measured scroll speed. A user flicking through at 3,000 px/s needs far more lead time than one reading slowly.

Directional prefetch

Scroll direction is information most implementations throw away. Someone scrolling down is going to reach the slots below them; the slots above are behind them and, unless they scroll back, will never be seen again.

The v7 engine uses this: it prefetches in the direction of travel and widens the trigger distance as scroll velocity increases, so fast scrollers get more lead time and slow readers do not have ads requested far ahead of where they are.

What not to lazy-load

  • Anything above the fold on load. It is already visible; delaying it only delays revenue.
  • The placement next to your LCP element. Lazy-loading it does not help LCP and complicates space reservation.
  • Slots in a container that is hidden and then revealed by script. IntersectionObserver and display: none interact badly; use explicit triggers instead.
  • Sticky or anchored units. They are always in view by definition.

Reading the results correctly

Measured impressions will fall, and that is the intended effect — you stopped counting impressions nobody saw. What should improve is viewability, revenue per impression, and page speed. Judge the change on revenue per pageview and viewability together. If impressions fell and revenue per pageview held while viewability rose, it worked.

Related questions

What root margin should I use for lazy loaded ads?

Between 200 and 600 pixels for most layouts, weighted higher on mobile where scroll velocity relative to viewport height is greater. The margin must cover your auction timeout plus ad server response plus creative render — if that totals 1.5 seconds and users scroll at 800 px/s, you need roughly 1,200 pixels of lead, not 200.

Does lazy loading improve viewability?

Usually yes, often substantially, because you stop requesting ads for slots users never reach — those were counted as impressions and dragged the average down. The gain only materialises if the trigger fires early enough for the ad to render before the slot is in view.

Should I lazy load above-the-fold ads?

No. They are visible on load, so delaying the request only delays revenue with no viewability benefit. If your concern is that the top ad slows the page, address it with space reservation, preconnects and a shorter timeout for that unit rather than by deferring the request.

Can I lazy load and refresh the same slot?

Yes, and they combine well: request on approach, then refresh on a timer while the slot remains in view. The rule is that a refresh must never fire for a slot that is out of view, otherwise you are generating unviewable impressions and buyers will notice in their viewability reporting.

Try it on your own site

Free up to 1 million ad impressions a month. You keep your Ad Manager account and your SSP contracts.

Start free