A reader opens an article on a phone, sees the headline, and starts reading. Then an ad appears above the headline and pushes everything down. The ad may have loaded correctly, but the page still feels broken.
The blunt fix is to reserve the maximum possible ad height everywhere. That can eliminate the jump, yet leave a large blank slab whenever a smaller creative—or no creative—arrives. The better fix is more deliberate: reserve space in the initial CSS, choose the reserved height per slot and breakpoint from the sizes that actually render, protect the first viewport more aggressively, and avoid collapsing a visible empty slot. Then judge the change with both field CLS and revenue data.
The original mobile lab included with this article shows the tradeoff. On the same 390×844 page, inserting a 250 px ad with no reservation produced a CLS of 0.15068. Reserving 100 px first reduced it to 0.07397. Reserving the full 250 px produced 0.00000—but that same 250 px reservation left 200 px unused when the simulated creative was only 50 px tall. These are controlled teaching results, not AdSense benchmarks or a promise that your page will produce the same scores.
The problem is geometry, not merely “slow ads”
Ads are usually requested asynchronously. The rest of the page can become visible while the request is still in flight; when the creative finally takes space in the document flow, content below it moves. Google’s Publisher Tag guidance identifies fetching, rendering, resizing, expansion, and collapse as points where an ad slot can create a layout shift, and warns that slots near the top of the initial viewport can displace a disproportionate amount of visible content (Google Publisher Tag: Minimize layout shift).
CLS does not simply add every movement forever. It measures the largest burst, or session window, of unexpected layout shifts during the page’s lifecycle. Google’s current guidance defines a “good” CLS as 0.1 or less at the 75th percentile, evaluated separately for mobile and desktop (web.dev: Cumulative Layout Shift). That percentile and lifecycle language matter. A clean reload on one fast phone is not proof that readers have a clean experience.
The browser records a layout shift when an already-visible element changes position between rendered frames. A late block added below everything currently visible may add no CLS at that moment. The identical block inserted above a visible headline can move most of the viewport. This is why an ad’s height alone cannot predict its CLS cost; slot position, viewport, timing, existing content, and user state all participate.
A controlled before-and-after: what reservation changed
The downloadable interactive CLS demo uses one fictional article, one viewport, and one simulated 1.2-second ad response. It makes no AdSense or third-party network request. A PerformanceObserver records layout-shift entries without user input, and the same seven scenarios were run three times in Chromium 144.0.7559.96 at 390×844 CSS pixels. The full measurement CSV, run record, and method note are included in the package.
| Same-page scenario | Initial reservation | Final slot | Unused reserved height | Mean CLS, 3 runs |
|---|---|---|---|---|
| No reservation → 250 px ad | 0 px | 250 px | 0 px | 0.15068 |
| Reserve 100 px → 250 px ad | 100 px | 250 px | 0 px | 0.07397 |
| Reserve 250 px → 250 px ad | 250 px | 250 px | 0 px | 0.00000 |
| Reserve 100 px → 50 px ad; keep slot | 100 px | 100 px | 50 px | 0.00000 |
| Reserve 250 px → 50 px ad; keep slot | 250 px | 250 px | 200 px | 0.00000 |
| Reserve 250 px → no fill; keep slot | 250 px | 250 px | 250 px | 0.00000 |
| Reserve 250 px → no fill; collapse in view | 250 px | 0 px | 0 px | 0.15068 |
Three practical conclusions follow.
First, reservation is not binary. The 100 px compromise absorbed part of the eventual height, so only the remaining 150 px expansion moved content. It reduced the score below 0.1 in this one lab, but that does not certify the page’s 75th-percentile field CLS.
Second, a larger reservation can trade movement for vacancy. Keeping 250 px for a 50 px creative prevented a shift but left 200 px unused. That may be acceptable above a high-value slot, or visibly wasteful in the middle of a short article.
Third, collapse is also movement. In the lab, removing a visible 250 px empty slot pulled the article upward and recreated the same 0.15068 score as inserting a 250 px ad into an unreserved slot. “Hide empty ads” is therefore not a complete policy. The timing and visibility of the collapse decide whether it helps.
Choose a reservation height from delivered sizes, not folklore
There is no universally correct AdSense placeholder height. Responsive display units choose a size from the space available and the user’s display, and can request a new size after an orientation change (AdSense: responsive behavior of display ad units). A reservation that is sensible for a narrow article rail may be wrong for a full-width desktop slot.
Treat each meaningful combination of slot × breakpoint × template as its own decision. Start with a table drawn from production observations:
| Field to record | Example value |
|---|---|
| Template and slot | Article / after paragraph 4 |
| Viewport bucket | 360–479 px |
| Rendered slot height | 50, 100, or 250 px |
| Filled or unfilled | Filled |
| Slot visible when resolved | Yes |
| CLS value and largest shifted element | 0.08 / article paragraph |
| Revenue context | Page RPM, viewability, impressions |
Google recommends using historical delivered-size data for multi-size Google Ad Manager slots and describes three basic choices: reserve the largest eligible size, the smallest, or the size most likely to serve. The largest is most protective but can leave blank space; the smallest preserves density but can still expand; a likely or intermediate height is a compromise that must be validated in field data (Google Publisher Tag: Minimize layout shift). For AdSense, the same decision logic is useful, but do not claim that AdSense exposes an identical Ad Manager delivered-size report. Record actual wrapper or iframe dimensions in your own real-user monitoring, and pair them with AdSense coverage and revenue reporting.
A simple synthetic distribution makes the tradeoff visible. Suppose a mobile slot renders 50 px tall on 65% of filled impressions and 250 px tall on 35%. These figures are fictional and are used only to demonstrate the calculation.
| Reserved height | Expected unused height per filled impression | Expected extra growth beyond reservation | Operational meaning |
|---|---|---|---|
| 50 px | 0 px | 70 px | Dense page; 35% of fills still need 200 px growth |
| 100 px | 32.5 px | 52.5 px | Pays some whitespace to reduce larger expansions |
| 250 px | 130 px | 0 px | No growth for these two sizes; largest vacancy cost |
For the 100 px option, expected unused height is 65% × 50 px = 32.5 px; expected growth is 35% × 150 px = 52.5 px. These averages are not expected CLS. CLS is nonlinear because only visible, unexpected movement counts and because the impact and distance fractions depend on page geometry. Use this table to compare space policy, then measure actual CLS.
A useful hierarchy is:
- Top-of-page or first-viewport slot: favor the largest common height, a fixed approved size, or no in-flow ad at that position. The cost of movement is highest where the reader is already looking.
- Mid-article slot likely to be visible before fill: reserve the most common height or a deliberate compromise, based on mobile and desktop distributions separately.
- Far-below-fold slot: a smaller reservation may be acceptable if the ad usually resolves before the reader reaches it, but slow networks and fast scrolling still need field testing.
- Fluid or genuinely indeterminate creative: keep it below the fold where possible. Google notes that a fluid GPT slot cannot be fully pre-sized and therefore inherently resizes when its creative is known (Google Publisher Tag: Minimize layout shift).
Reserve the space before the ad code runs
The reservation must participate in the browser’s first layout. Google recommends min-height and min-width, with media queries where breakpoints need different minimums, and specifically cautions that adding the reservation later with JavaScript can itself create a shift (Google Publisher Tag: Minimize layout shift). The general web guidance is the same: reserve space for ads and other late-loaded content in the initial layout (web.dev: Optimize Cumulative Layout Shift).
Here is a starting pattern for a manually placed responsive AdSense unit. The heights are examples, not recommended universal values; replace them with your measured slot policy.
<div class="ad-frame ad-frame--article-top" aria-label="Advertisement">
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
data-ad-slot="YYYYYYYYYY"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
</div>
/* Load this with the page's initial CSS, before the ad resolves. */
.ad-frame {
width: 100%;
min-height: 100px;
}
@media (min-width: 768px) {
.ad-frame--article-top {
min-height: 250px;
}
}
min-height is usually safer than forcing the wrapper to one immutable height: it reserves a floor while still allowing a larger unit to fit. A hard height can be appropriate when you intentionally use an exact fixed-size unit, but do not clip or cover a creative to preserve your layout.
Keep the reservation rule in critical or otherwise early CSS. A React effect, tag-manager callback, or late component class change is too late if readable content has already painted. On server-rendered sites, emit the slot class and breakpoint policy in the initial markup. On client-routed pages, make sure the destination skeleton already contains the reservation before article content is revealed.
aspect-ratio can reserve responsive space when the content has a predictable shape. It is not a magic answer for a slot that may be 320×50, 320×100, or 300×250; those are materially different ratios. Use it only when your unit or creative family really follows a known ratio.
AdSense also documents approved responsive-code modifications for a fixed height with expandable width and for exact sizes selected with media queries (AdSense: How to modify your responsive ad code). Those options can make geometry more predictable, but fixed sizes can have less inventory and may reduce earnings in some cases, so compare the result rather than assuming it is free (AdSense: Guidelines for fixed-sized display ad units).
Treat no-fill as a layout decision
After an AdSense request, the <ins> element receives a data-ad-status value such as filled or unfilled. AdSense says it collapses an unfilled unit only when doing so will not cause page reflow—specifically, when the unit is outside the viewport—and otherwise retains the unit’s size as blank space (AdSense: Use the data-ad-status parameter to hide unfilled ad units). That behavior reflects the same tradeoff shown in the lab. Google also documents a separate unfill-optimized state when its Fill empty in-page ads setting handles an empty slot. Record that state separately rather than treating every non-filled response as an ordinary unfilled unit.
Choose among three policies deliberately:
| No-fill policy | CLS risk | Space/revenue implication | Best fit |
|---|---|---|---|
| Keep the reserved box | Lowest immediate reflow risk | Visible blank area | Slot already visible; stability is the priority |
| Collapse only before it becomes visible | Low if genuinely outside the viewport | Recovers page space | Far-below-fold slot with reliable timing and observation |
| Replace with useful house content | Stable if replacement fits the same box | Uses the inventory for newsletter, app, or navigation | You have a suitable first-party fallback and policy approval |
The dangerous shortcut is a global rule that hides every data-ad-status="unfilled" unit and also removes its parent’s reserved height. It may tidy screenshots while producing an upward jump for readers who already saw the slot. If you customize no-fill behavior, log whether the box was in the viewport when its state changed and test both slow and fast responses.
A house fallback should fit inside the reserved dimensions and be labeled appropriately. Do not swap in a taller fallback after the ad decision; that simply replaces one late expansion with another.
Auto ads need placement control, not one placeholder rule
A manual ad slot gives you a stable DOM location to reserve. In-page Auto ads can select placements from the page’s layout and content, so a single hard-coded placeholder cannot pre-size every location Auto ads might choose.
Use the controls AdSense does provide. The Auto ads settings let publishers turn formats on or off, limit the maximum number of in-page ads, set minimum distance between them, and preview placements. Overlay formats are placed over the page rather than within its layout, so they do not create the same in-flow displacement described here; they still require usability and performance testing (AdSense: Auto ads settings).
For layout-critical regions, exclude the area from in-page Auto ads. AdSense explicitly gives “immediately below the header” as an example, and its preview shows candidate areas by pushing content aside. Exclusions depend on CSS selectors, so a template refactor can invalidate them; recheck after structural releases (AdSense: Exclude areas from in-page Auto ads).
A practical hybrid is often easier to control:
- Use manual, pre-reserved units in the first viewport and other editorially sensitive positions.
- Allow Auto ads only in less fragile regions, with excluded areas around the header, title, purchase controls, or other critical UI.
- Compare Auto ads configurations against a stable baseline using both field CLS and monetization metrics. Do not infer a revenue answer from CLS alone, or a UX answer from RPM alone.
Test the page as an operator, not as a screenshot
A layout fix is ready only after it survives the ad outcomes readers actually receive.
1. Trace the shift locally
Open Chrome DevTools’ Performance panel, reload under a representative mobile viewport and network condition, and inspect the Layout Shifts track. The panel shows live CLS and lists shifted elements, timing, and scores; it can also compare local measurements with available Chrome UX Report field data (Chrome DevTools: Performance panel). Remember that the element reported as shifted is often the victim, not the cause: if the headline moved, inspect the slot above it.
Run at least these cases at each important breakpoint:
- common small creative;
- common large creative;
- no fill retained;
- no fill collapsed before visibility, if you use that policy;
- delayed consent or delayed ad request;
- cold cache and throttled network;
- portrait-to-landscape change;
- rapid scroll toward a below-fold slot;
- client-side route change, if applicable.
2. Record real-user CLS with slot context
PageSpeed Insights combines controlled Lighthouse lab data with CrUX field data when enough real-user samples exist. Its field view covers the previous 28-day collection period and may fall back to origin-level data—or show none—when a URL lacks sufficient samples (Google: About PageSpeed Insights). That makes PSI useful for trend confirmation, but often too aggregated to select a reservation height by itself.
For faster diagnosis, send your own real-user measurements with a release identifier, template, breakpoint, slot names, fill state, and observed rendered height. Google’s web-vitals package includes an attribution build that adds diagnostic information to the metric object (GoogleChrome/web-vitals).
import {onCLS} from 'web-vitals/attribution';
onCLS(({value, rating, attribution}) => {
navigator.sendBeacon('/rum', JSON.stringify({
metric: 'CLS',
value,
rating,
largestShiftTarget: attribution?.largestShiftTarget,
largestShiftTime: attribution?.largestShiftTime,
template: document.body.dataset.template,
release: window.APP_RELEASE
}));
});
Add your own slot observations when each unit resolves. Avoid collecting article text, user identifiers, or other unnecessary payloads. Also note a measurement boundary: the Layout Instability API used by page-level RUM does not report shifts that occur inside iframes, while CrUX includes iframe content in the user-experience metric (web.dev: Cumulative Layout Shift; web-vitals limitations). Movement of the iframe element itself can still affect the host page. Use browser field data and your page-level observations together rather than treating one diagnostic feed as complete.
3. Keep a revenue guardrail
Evaluate a release over a long enough period to cover normal traffic and demand variation. Segment at least by device class, template, and meaningful slot. Watch:
- 75th-percentile field CLS;
- distribution of rendered heights and no-fill rate;
- page RPM and ad impressions per pageview;
- viewability for the changed slot;
- reader outcomes that matter to the site, such as engaged time, subscription starts, or commerce conversion.
A fixed-height unit may improve stability while reducing the range of eligible ads. A maximum reservation may protect CLS while pushing the first paragraph farther down. A smaller reservation may recover density while reintroducing movement. The goal is not the lowest isolated number; it is a stable page with an explicit, measured monetization tradeoff.
A shipping rule for each slot
Before release, give every important slot a one-line policy:
| Slot condition | Default action |
|---|---|
| In the initial viewport, variable sizes | Reserve the largest common height or move/remove the in-flow placement |
| In the initial viewport, exact approved size | Reserve that exact height in initial CSS |
| Mid-article, one size dominates | Reserve the dominant height; center smaller creatives visually if implementation permits |
| Mid-article, sizes are split | Test an intermediate minimum and quantify both growth frequency and empty space |
| Far below the fold | Use a measured minimum; resolve early enough that most readers never see a resize |
| No fill while visible | Keep the reservation or replace it with same-height first-party content |
| No fill outside the viewport | Collapse only after verifying that the operation does not create visible reflow |
| Auto ad near critical UI | Exclude the area or replace it with a controlled manual slot |
Then attach three acceptance checks: the slot’s common and worst relevant outcomes have been traced locally; field instrumentation can identify the release and slot context; and the monetization owner has a before/after report that uses the same definitions and date window.
Reserve deliberately, not maximally
Preventing ad-related layout shift is straightforward at the CSS level and nuanced at the product level. Put a minimum box in the first layout. Size it from real delivered outcomes at that slot and breakpoint. Spend more space to protect the first viewport, and accept more experimentation farther down the page. Do not erase a visible no-fill box merely because blank space looks inefficient. Constrain or exclude Auto ads where their placement conflicts with the page’s structure.
Most importantly, separate the claims. A local trace can prove that one implementation moved less under one condition. Field CLS can show what real users experienced across devices and sessions. Revenue reporting can show the monetization consequence. You need all three before calling the reservation “right.”
Sources
- Minimize layout shift — Google Publisher Tag, Google for Developers; last updated July 31, 2025.
- Cumulative Layout Shift (CLS) — web.dev.
- Optimize Cumulative Layout Shift — web.dev.
- About the responsive behavior of display ad units — Google AdSense Help.
- How to modify your responsive ad code — Google AdSense Help.
- Guidelines for fixed-sized display ad units — Google AdSense Help.
- Use the data-ad-status parameter to hide unfilled ad units — Google AdSense Help.
- Auto ads settings — Google AdSense Help.
- Exclude areas on your pages from showing in-page Auto ads — Google AdSense Help.
- Performance panel: Analyze your website’s performance — Chrome for Developers.
- About PageSpeed Insights — Google for Developers.
- GoogleChrome/web-vitals — GoogleChrome on GitHub.




