LFW

Insights · What the free audit finds · 7 minutes

Why the Page Jumps While It Loads

What layout shift is and why it registers as brokenness, the four causes behind nearly every case (images without dimensions, embeds without reserved space, fonts swapping, banners injected late), and the exact fixes, including where WordPress already knows every image's width and height.

By Leland Fiegel · Web operations, LFW · September 2, 2026

What layout shift is

Layout shift is content moving after it has been drawn. The browser paints the page with the information it has, then an image arrives at a size it did not know about, and everything below it moves down to make room. Or a font loads and every line re-wraps. Or a notice bar inserts itself at the top and shoves the page two inches.

It is measured as Cumulative Layout Shift, the sum of every unexpected movement during loading, and a score under 0.1 is considered good. Visitors do not know the number. They know they tapped a link and hit the one beneath it.

Cause one: images without dimensions

The most common cause by far. An img tag with a src and nothing else tells the browser nothing about the image's shape, so it reserves zero height until the file downloads. The fix is two attributes: width and height. They do not force the image to that size on screen; with the usual CSS of max-width 100% and height auto, they simply tell the browser the aspect ratio, and it reserves the right box before the first byte arrives.

WordPress already knows these numbers for every file in the media library. Open any image in Media and the details panel shows its dimensions, 1920 by 1080 pixels for instance. Images placed through the editor get width and height written into the tag automatically. Where it goes wrong is the theme: hero images set through a page builder, a customizer field, or a hand-written template often output the URL alone. In a template, ask WordPress for the image instead of building the tag by hand, and the attributes come with it:

<?php
// Right: WordPress writes src, srcset, sizes, width, height, and alt for you.
echo wp_get_attachment_image( $hero_id, 'full', false, array(
  'class'         => 'hero-img',
  'fetchpriority' => 'high',
  'loading'       => 'eager',
) );

// If you must build the tag yourself, take the real dimensions from the library:
$img = wp_get_attachment_image_src( $hero_id, 'full' ); // [ url, width, height ]
printf(
  '<img src="%s" width="%d" height="%d" alt="%s" />',
  esc_url( $img[0] ), $img[1], $img[2], esc_attr( get_post_meta( $hero_id, '_wp_attachment_image_alt', true ) )
);

Then the one line of CSS that makes the attributes behave on every screen size:

The CSS that goes with it

With width and height present, this rule lets the image scale to its container while the browser keeps the reserved box the right shape. Most themes already have it; check that nothing overrides height to a fixed value.

img {
  max-width: 100%;
  height: auto;
}

/* A hero set as a CSS background can't carry dimensions. Give its box a shape instead: */
.hero {
  aspect-ratio: 16 / 9;
  background-size: cover;
}

Cause two: embeds without reserved space

A YouTube or C-SPAN iframe arrives with whatever height the embed code specified, often 315 pixels at a 560 width, and then a responsive theme stretches the width without adjusting the height, or the player script resizes it after load. Either way, the page moves.

Wrap every video embed in a box that has its shape declared, and let the iframe fill it. The space is reserved before the player loads, and it scales with the column.

.embed {
  aspect-ratio: 16 / 9;   /* 4 / 3 for older video, or match the embed's own ratio */
  width: 100%;
}
.embed iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

Cause three: fonts that swap

A web font arriving after the page has painted re-wraps every line set in it, and if the fallback font is a different width, whole paragraphs change height. Preloading the font file gets it there before the first paint on most connections. The size-adjust descriptor lets you tune the fallback so that the swap, when it does happen, changes almost nothing.

<!-- in the head: fetch the font before the page needs it -->
<link rel="preload" href="/fonts/body.woff2" as="font" type="font/woff2" crossorigin />

/* in the CSS: a fallback shaped to match, so the swap barely moves anything */
@font-face {
  font-family: "Body Fallback";
  src: local("Arial");
  size-adjust: 104%;
  ascent-override: 92%;
}
body { font-family: "Body", "Body Fallback", sans-serif; }

Cause four: things inserted at the top after the page paints

Cookie notices, announcement bars, and alert banners injected by JavaScript are the last common cause, and the easiest to see: the whole page drops the moment the bar appears. Two fixes, and either works. Render the bar in the page's HTML so its space exists from the first paint, or make it an overlay that sits on top of content instead of pushing it down. What never works is inserting a block above content after the content has been drawn.

Seeing it yourself

In Chrome, open the Performance panel and record a page load; each shift is marked and the element that moved is named. Lighthouse reports the total as Cumulative Layout Shift. The free audit on this site runs the same measurement on your homepage and tells you in plain English whether the page moves.

If you are a client, fixing a template's hero image or embed box is a small task under the retainer. A theme that builds every image tag by hand is a project, quoted fixed. Open a request and we will find the element that is moving first.

Keep reading

September 2, 2026 · 7 minutes

Phones Download Your Desktop Images

Why a 2.6 MB hero reaches a phone that needs 33 KB, how srcset and sizes let the browser choose, when you actually need the picture element, what WebP buys you, and where WordPress themes quietly undo the work it already does for you.

September 2, 2026 · 5 minutes

Your Media Library Is Public

The media library is not a private folder, in WordPress or anywhere else. What that means for the drafts and packets that get uploaded to grab a link, why switching the API off is the wrong fix, and how to mark a file private without breaking anything.

Prefer to write?

Tell us what needs to work better.

Slow, fragile, hard to edit, missing a workflow. Say it plainly, and you'll get a straight answer, not a ticket number.

You'll get a straight answer, usually the same day.