How to Fix Layout Shift on Article Pages: A 2026 Guide

Cumulative layout shift (CLS) is a unitless Core Web Vitals score that measures how far visible content moves unexpectedly while a page loads and while a reader scrolls it. A score of 0.1 or less at the 75th percentile of real visits is the pass mark. Learning how to fix layout shift on article pages comes down to one habit: reserve the space before the content arrives, then never let anything change it.

Article templates fail more often than landing pages because they are long, full of media, and stitched together from components that were each built in isolation. The hero image has no dimensions. An ad slot collapses until the ad network answers. A web font swaps in and every line of body copy reflows. A sticky header grows a second row on scroll. None of that shows up on your homepage, which is why a story can sit at 0.31 in field data while a Lighthouse run on the same URL says 0.04.

This guide walks through the order I would use: reproduce the shift, find the element causing it, reserve space for media, stabilize fonts, fix the dynamic components, then lock the result in with tests. Most of the work is in the template, not in the content, which means one fix can repair thousands of URLs at once.

What You Need

What You Need

You do not need much. You need to see the shift and you need permission to change the template.

Tools

  • Chrome DevTools with the Lighthouse panel, or the standalone Lighthouse CLI. This is where you see individual shift entries and the Layout Shift Culprits insight, which names the elements that moved.
  • PageSpeed Insights, for the side-by-side comparison between lab data and field data. The Origin and CrUX summary panels are the ones that matter here.
  • A real article URL with no cookie banner covering it, ideally one you can reload many times without hitting a paywall or a consent dialog.

Access

  • The article template itself, including any page-builder layout, block markup or theme overrides that wrap it.
  • The components inside the body copy: the image and video blocks, the in-article ad wrapper, the newsletter form, the related-story module, the comments block.
  • Enough access to change CSS and markup. If the template is locked behind a page builder, know that before you start, because most of the fixes below need a class you can add or a wrapper you can size.

Representative article URLs

Pick five to ten stories that share the template: one text-only, one image-heavy, one with embeds, one with ads, one long-form feature, one live or continuously updated page. Fixing a single URL proves nothing. A template is the unit of repair, and you need the range of stories that use it.

Reproduce before you change anything. Load each URL a few times on a throttled connection and write down which ones shift, how much, and roughly when. Without that baseline you cannot tell whether a change helped or whether you just got a quieter network.

Step-by-Step

Step-by-Step

Work through these in order. Each step assumes the previous one is done, because measuring after every fix is the only way to know what is still moving.

Step 1: Measure the Layout Shift on Article Pages

Open the article URL in Chrome, open DevTools, switch to the Performance panel, and record a fresh load with CPU throttling on. In the track area near the bottom of the flame chart you will find the Layout Shifts track. Blue clusters are unexpected shifts; grey ones had recent input and are excluded from the score.

Click a shift cluster and the details panel lists the elements that moved, with their old and new positions. The Layout Shift Culprits insight in the Lighthouse panel ranks those elements for you, which saves a lot of guessing when a story has four moving things at once.

Now separate the two kinds of shift. Load CLS happens while the page is still arriving, and it is usually images or fonts. Post-load CLS happens after the load event, and it is nearly always a component: an ad, a widget, a consent banner, a header that changes height. DevTools marks both, and they have completely different fixes.

Then compare with reality. Run the same URL through PageSpeed Insights and look at the field data section. If CrUX reports a failing 75th percentile score while your Lighthouse run passes, the difference is not noise. Lab data is one load on one machine; field data is every visit across thousands of phones and networks, including the ones where the ad request is slow, the font CDN is slow, and the reader scrolls before the page settles.

A word on the arithmetic, because it explains the aggregation. Each shift scores as impact fraction multiplied by distance fraction. Impact fraction is the share of the viewport affected, distance fraction is how far those elements moved relative to viewport height. Those scores are summed into clusters, and CLS reports the largest burst rather than the page total: a new cluster starts after a one-second gap and the window caps at five seconds. That session-window rule arrived in 2021, which is why numbers in older guides do not match what you see today.

How to confirm this step: you can name at least one element per shift cluster, and you know whether the score you are chasing came from load or post-load behaviour.

Step 2: Reserve Space for Images and Video

An image with no intrinsic size is the single most common cause of layout shift on article pages. The browser reserves nothing, the text below it sits at the top of the box, and when the file arrives everything under it jumps down.

Set explicit width and height attributes on every image. The browser divides them to get an aspect ratio and reserves the box before a byte of the image downloads.

<!-- shifting -->
<img src="photo.jpg" alt="Poll workers counting ballots">

<!-- stable -->
<img src="photo.jpg" alt="Poll workers counting ballots"
     width="1600" height="900">

The attributes are the ratio, not the rendered size. Pair them with CSS so the image can still be fluid without losing the reservation.

.article-body img {
  width: 100%;
  height: auto;
}

Where a CMS or page builder writes the image through a styled wrapper, add aspect-ratio on the container instead. That works even when the image tag itself is out of your control.

.wp-block-image img,
.article-media img {
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;
  object-fit: cover;
}

For responsive images, keep the ratio consistent across breakpoints unless you are art-directing on purpose. If the mobile crop is a different shape from the desktop crop, put width and height on the <source> elements inside the <picture> too, so each branch of the srcset declares its own ratio. Art direction is legitimate and often better looking; it just has to be declared, otherwise the phone layout shifts every time the picture element swaps branches.

Do the same for video and embeds. A YouTube block or a tweet card needs a container with a fixed aspect ratio before the iframe loads. With srcset and lazy loading still on, this matters more, not less, because lazy loading means the shift happens later and lower on the page where the reader is already reading.

One publisher-facing note: if your CMS has separate fields for image width and height, fill them in and make the field required. Template discipline beats template rules that nobody follows.

How to confirm this step: reload the article with images disabled in DevTools. The page should hold its layout, with empty boxes where the media will go, and the CLS should be near zero for image-related entries.

Step 3: Stabilize Fonts and Late-Loading Content

A web font that arrives after first paint swaps in and reflows every line beneath it. On a long article that is thousands of words moving at once, and it is the shift readers notice most because they are mid-sentence.

Start with the strategy. font-display: optional lets the browser use the fallback for that page load and only swap in the real font on the next one, which removes the shift entirely at the cost of some readers seeing the fallback. font-display: swap shows the fallback immediately and swaps, which is more readable but shifts. font-display: block hides text briefly, which is the flash of invisible text (FOIT) problem.

Then make the fallback occupy the same space as the web font. The browser can do the maths for you if you supply the metric overrides, and this is the single highest-value line in most newsroom CSS.

@font-face {
  font-family: "Article Serif Fallback";
  src: local("Georgia");
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
  size-adjust: 107%;
}

.article-body {
  font-family: "Article Serif Fallback", "Article Serif", Georgia, serif;
}

The overrides make the fallback’s ascent, descent and line gap match the web font, and size-adjust scales the glyphs to match the average width. Copy the percentages from the font-loading API rather than guessing, then compare rendered line heights in DevTools with the network throttled until the font arrives. If the line box height does not change when the font swaps, the reservation is right.

Preload the body font and only the body font. Preloading four weights means four connections competing with your hero image, which is a bad trade. Self-hosted, subsetted woff2 files served from your own origin beat a third-party font service for both speed and predictability, and they remove the third-party failure mode entirely.

If your editorial design permits it, a system font stack for body copy is the zero-risk option. Headlines can keep the web font, because a headline is one or two lines and the reflow cost is small.

How to confirm this step: throttle the network in DevTools, reload, and watch the text. No visible jump in the body copy means the metric overrides are doing their job.

Step 4: Fix Dynamic Article Components

This is where article templates get their post-load CLS, and where the generic advice stops being useful.

In-article ads

Reserve a fixed or bounded height on the slot container and never let it collapse to zero while the request is in flight.

.ad-slot {
  min-height: 250px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f4f4f4;
}
@media (min-width: 768px) {
  .ad-slot { min-height: 280px; }
}

Give the slot different heights per breakpoint if the ad sizes differ, and keep the reserved height through the no-fill case. When the ad network returns nothing, the reserved space stays. An empty grey box mid-article is a design decision; a hole that collapses is a layout shift.

Where the ad unit height is genuinely uncertain, move the slot further down the story. Content that loads below the reader’s current position costs you less than content that loads under their thumb.

Newsletter forms and paywalls

An inline signup box that injects itself after three seconds is a jump in the middle of a paragraph. Load it in place from the start, or render a skeleton of the same dimensions. A modal or interstitial that appears over the article is fine for CLS as long as it does not push the page down, so use fixed or overlay positioning rather than in-flow insertion.

Sticky headers

A sticky header that adds a compact row, a ticker or a progress bar on scroll changes height and drags everything under it. Give it a fixed height at every breakpoint and let the inner elements change inside that height. If the design requires a taller sticky header, reserve the taller height from the first paint instead of animating into it.

Every one of these is a request that finishes after the article does. The same rule applies: reserve the box, or push the request to a user action. A “Load more comments” button that the reader clicks is user-initiated and excluded from the score. Auto-loading comments as the reader nears the bottom of the page is not, and it is one of the worst offenders on long stories because the reader is still reading the paragraph above.

Scroll-triggered behaviour

Never lazy-load anything a reader can reach, and avoid scroll-triggered entrance animations that change element size. Animate with transform (translate, scale) rather than top, left, width or height, because transform animations run on the compositor and skip layout and reflow entirely. Keep will-change on a small number of elements, not sitewide, and drop it when the animation ends.

Continuously updated and live pages

Live blogs and stories with corrections get new blocks inserted mid-page. That is a shift you cannot reserve for in advance, so insert new content at the top with a defined order, or push updates into a live region above the article body without reflowing the article itself.

How to confirm this step: record a load with the network slowed to roughly 3G and scroll the whole article slowly. Nothing under your cursor should move once it has settled.

Step 5: Test and Prevent Regressions

Ship the fixes, then verify in two directions. Lab first, because it is immediate: run Lighthouse on the article template in mobile and desktop presets and compare against your baseline. Several runs, not one, since scores vary run to run.

Field second, because it is the number that counts. Core Web Vitals field data is aggregated from real Chrome users through the Chrome UX Report and rolls into Search Console on a rolling 28-day window. That means a fix shipped today shows up gradually over the following weeks, and a single bad week stays in the average for a month. Do not read a static field number as a verdict on a change you made yesterday.

CLS at the 75th percentileRatingWhat it means for an article template
0.1 or lessGoodPassing. Focus remaining effort on the post-load shift you see in field data.
Above 0.1 up to 0.25Needs improvementOne unsized hero or one collapsing ad slot is usually enough to put you here.
Above 0.25PoorMultiple unsized media, fonts and injected components. Fix in the order below.

Set acceptance criteria that a template must keep passing rather than chasing a number for every page. A short Lighthouse budget in CI on the article template, a check that every image emitted by the CMS carries width and height, and a monthly CrUX read on the article section together will hold the line. Editors adding a new block type is the usual regression source, so put the check in the pull request rather than in a quarterly audit.

If your stack allows it, a small PerformanceObserver snippet on the article template reports which elements shift for real visitors, grouped by template. It turns a vague field number into a queue of named selectors.

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.hadRecentInput) continue;
    console.log('CLS', entry.value, entry.sources.map(s => s.node));
  }
}).observe({ type: 'layout-shift', buffered: true });
OrderFixEffortTypical recovery
1Dimensions on the hero and inline imagesHoursLargest single reduction on most templates
2Reserved height on ad slotsHalf a dayRemoves most post-load shift
3Metric-matched font fallbackHalf a dayRemoves load-time text reflow
4Fixed-height sticky headerHoursSmall but certain on long articles
5Newsletter and paywall injectionA dayVaries with how often it fires
6Comments, related stories, embedsOne to two daysDepends on how far down they sit

How to confirm this step: the template passes your Lighthouse budget on mobile and desktop, and the CrUX trend for the section moves the right way over the following month.

Common Mistakes

Adding height attributes to the image tag but not the container. If a page-builder wrapper has its own padding or a fixed height, the attributes calculate a ratio the browser never uses. Check the rendered box in DevTools, not the markup.

Reserving too much space. A 600-pixel box for a 250-pixel ad is a design decision your readers will notice. Reserve the real height, or use a bounded minimum with a fixed maximum so an oversized creative cannot blow the page apart either.

Inserting ads without a slot. The fix is a wrapper with a minimum height in the template, not a promise to the ad team. If the wrapper lives in the ad tag itself, nothing is reserved before the tag loads.

Loading fonts with no fallback strategy. A late swap with default metrics moves every line below it. Without the ascent, descent, line-gap and size-adjust overrides you have moved the problem, not solved it.

Testing only the homepage. The homepage has a short scroll, no in-body ads, and probably no embeds. If your field CLS comes from article URLs, homepage testing tells you nothing.

Chasing unrelated metrics. Layout shift is one of three Core Web Vitals, alongside Largest Contentful Paint (LCP) and Interaction to Next Paint (INP), and each has its own fixes. Reserving space for images helps CLS and can help LCP; collapsing an ad slot does nothing for either. Fix the metric you are measuring.

Declaring success from a single Lighthouse run. One run is one load on one machine. Run it three times, on both presets, and check field data before you call it done. And be clear with your team about what CLS is: Core Web Vitals are signals that feed Google’s page experience systems, not direct ranking factors you can move traffic with on their own.

A note worth giving an editor who is about to argue with you: a shift within 500 milliseconds of a reader interaction is excluded from the score, and so is a change in size when nothing else moved. Fixes that chase those cases cost time and recover nothing.

Frequently Asked Questions

Is layout shift usually caused by the CMS or the theme?

Usually the components, which live between the two. A CMS image block that never asks for width and height, or a theme without an aspect ratio on the media wrapper, will each produce shifts on every story. Page builders add a third layer: their own injected CSS above the fold. The fastest way to tell is to inspect the computed height of the moving element in DevTools and look at which file the rule came from. If the answer is generated markup, fix it in the template; if it is builder CSS, scope your correction to the article template rather than resetting styles sitewide.

What CLS score should an article page aim for?

0.1 or less at the 75th percentile, which is the good threshold for real-user field data. For a lab run, stay comfortably under it so slow devices and slow ad requests have headroom. Aiming at 0.05 in Lighthouse is a reasonable internal target for a template that has ads. Do not chase a perfect zero, because a small amount of shift is normal and the last few hundredths usually come from elements outside your control.

Do images still need width and height attributes?

Yes. The attributes are how the browser learns an aspect ratio before the file arrives, and that ratio is what reserves the box. Modern CSS gives the same result: an aspect-ratio declaration on the image or its wrapper, paired with height auto, works even where attributes are stripped from the markup. What matters is that the ratio is declared somewhere before the bytes arrive. Width and height attributes remain the easiest and most widely supported way to do it, especially inside picture elements for art-directed images.

How do I prevent ads and embeds from shifting article content?

Reserve the space in the template before the request goes out. Wrap each ad slot in a container with a fixed or minimum height, give that container different heights per breakpoint if the ad sizes differ, and keep the reserved height even when the slot fills with a blank. For embeds, put the iframe inside a container with a fixed aspect ratio. When the third party will not give you a guaranteed size, push the slot further down the article so the shift happens out of the reader’s way.

Does fixing layout shift improve Core Web Vitals?

It improves one of the three directly. CLS is the visual stability Core Web Vital, and passing it means your field data reports good scores for that metric. Fixing shifts also tends to help Largest Contentful Paint, because reserving image space lets the browser start rendering sooner. It does not move Interaction to Next Paint on its own. Remember that these are signals feeding page experience evaluation rather than direct ranking factors.

How often should I test article-page layout shift?

Run a Lighthouse check whenever you change the article template, and read field data monthly per section. Field data is aggregated through the Chrome UX Report on a rolling 28-day window, so a fix shipped this week shows up gradually and a bad week stays in the average for about a month. Weekly checks make the trend noisy rather than informative. Add a CI budget check on the template so regressions are caught at pull request time instead of in the next audit.

Conclusion

Start with images. Put width and height on every editorial image in the template, then add a minimum height to every ad slot wrapper. Those two changes take the largest share of the score off most article templates and cost a day between them.

Then move through fonts, the sticky header, and the injected components, checking with DevTools after each one. Once the template passes your Lighthouse budget on mobile and desktop, keep a CI check on it and read the CrUX trend for your article section once a month, knowing the number will lag your changes by weeks rather than days.

Leave a Comment