Core Web Vitals: How Missing Images Inflate Your CLS Score
Understand why missing or slow-loading images inflate Cumulative Layout Shift and how dimension-matched placeholder URLs eliminate the CLS penalty at zero cost.
Cumulative Layout Shift (CLS) measures how much visible content moves after the browser has rendered it. Images without explicit dimensions are the single most common cause of CLS above the 0.1 threshold. When a browser renders an img tag without width and height attributes, it reserves zero pixels for the element until the image file arrives — at which point everything below the image shifts down.
The fix for dimensionless images is to add width and height attributes, use aspect-ratio CSS, or provide a same-size placeholder URL as the initial src. Any of these three approaches prevents the 0-to-full-height expansion that scores against CLS. Placeholder URLs from fallback.pics are deterministic and cacheable, making them a practical complement to the attribute-based approach.
How CLS is scored
What Google counts as a layout shift
CLS accumulates whenever a visible element moves unexpectedly within the viewport after the first input delay or 500ms after the page starts rendering. The score is the sum of individual layout shift scores, where each shift score is the impact fraction (fraction of viewport affected) multiplied by the distance fraction (how far the element moved relative to viewport height).
An image loading late on a page with no reserved space is a textbook CLS event. If the image is 600px tall and the viewport is 800px, the distance fraction is 600/800 = 0.75. If the image is 100% wide (full viewport width), the impact fraction is high. These two factors multiply to create a large layout shift score.
CLS above 0.1 is rated 'Needs Improvement' by Lighthouse and PageSpeed Insights. Above 0.25 is 'Poor'. Both Google Search Console and Chrome User Experience Report (CrUX) track CLS in the field, meaning real user experiences with layout shifts affect your site's Core Web Vitals status in search.
Root cause
Why images without dimensions cause 0-height reserves
When the browser parses an `<img>` tag without width and height attributes, it has no way to know the image's dimensions until the file download begins (HTTP headers include dimensions for some formats) or the file is decoded. Modern browsers attempt to infer dimensions from image format headers early in the download, but this is not reliable for all formats or network conditions.
The result is that the browser reserves 0px × 0px for the image element during initial layout. Content below the image renders at the top of the available space. When the image finally loads and expands to its natural dimensions, all subsequent content shifts downward. This is the classic layout shift from images.
CMS-delivered images are particularly prone to this problem because developers do not always know the final image dimensions when writing the template. A product photo could be 600×400 or 1000×700 depending on what the merchandising team uploads. Explicit width and height attributes require knowing the expected aspect ratio.
Fix 1: attributes
Width and height attributes are the primary fix
Adding `width` and `height` attributes to `<img>` tags is the most direct CLS fix. These values tell the browser the image's intended display size before any network bytes arrive. The browser can reserve the correct layout space immediately. If you use CSS to make images responsive (e.g., `width: 100%`), browsers from 2019 onward will use the aspect ratio from the attributes to maintain proportional space.
You do not need to know the exact pixel dimensions of every image. You need to know the aspect ratio. A 600×400 image and a 1200×800 image both have a 3:2 aspect ratio. Setting `width=600 height=400` works correctly for both if your CSS constrains the display size.
<!-- Breaks layout: no width/height, browser reserves 0px height -->
<img src="product.jpg" alt="Product" />
<!-- Fixes CLS: browser reserves correct space before image loads -->
<img src="product.jpg" alt="Product" width="600" height="400" />
<!-- Also works: aspect-ratio CSS with a known ratio -->
<style>
.product-img { aspect-ratio: 3/2; width: 100%; }
</style>
<img class="product-img" src="product.jpg" alt="Product" /> Fix 2: placeholder URLs
Placeholder URLs prevent CLS for async-loaded content
Width and height attributes handle CLS for statically known images. For dynamically loaded content — search results, infinite scroll, lazy-loaded cards — there is often a moment when the img src is undefined while the component data is loading. During this moment, an img with no src can cause a 0-height element that causes a layout shift when the real src arrives.
Using a placeholder URL as the initial src while the real URL loads prevents this. The placeholder is served immediately from the CDN, so the browser receives the image dimensions from the response and reserves the correct space. No layout shift occurs when the real image replaces the placeholder.
The tradeoff is a network request for the placeholder URL. For content above the fold, where CLS is most impactful, this request cost is worth the CLS improvement. For below-the-fold content with `loading=lazy`, the request does not fire until the user scrolls, so the impact is minimal.
// React: use placeholder URL while data loads
function ProductCard({ product }) {
const src = product
? product.imageUrl ?? `https://fallback.pics/api/v1/400x300?text=${encodeURIComponent(product.name)}`
: 'https://fallback.pics/api/v1/400x300?text=Loading';
return (
<img
src={src}
alt={product?.name ?? 'Loading'}
width={400}
height={300}
/>
);
} Fix 3: aspect-ratio CSS
The aspect-ratio property for responsive images
The CSS `aspect-ratio` property maintains the correct height as the image width changes. Combined with `width: 100%`, it produces an intrinsically sized container that reserves space correctly without knowing pixel dimensions in advance.
Use `aspect-ratio` on a wrapper element or directly on the `<img>` tag. For product grids where all images should be the same ratio, apply `aspect-ratio: 1` (square) or `aspect-ratio: 4/3` at the component level. For article hero images, use `aspect-ratio: 16/9` or your editorial layout ratio.
The tradeoff with pure CSS aspect-ratio (no placeholder URL): the browser reserves the correct space, but the area is blank until the image loads. For above-the-fold hero images, a visible placeholder is better UX than a blank white space. For below-fold content, blank space during lazy loading is acceptable.
/* Universal image CLS prevention */
img {
aspect-ratio: attr(width) / attr(height); /* Use intrinsic ratio from attributes */
width: 100%;
height: auto;
}
/* Grid cards with known 3:4 aspect ratio */
.product-card img {
aspect-ratio: 3/4;
width: 100%;
object-fit: cover;
}
/* Hero image with 16:9 ratio */
.hero-image {
aspect-ratio: 16/9;
width: 100%;
overflow: hidden;
} Measurement
Measure CLS from images before and after
Use Chrome DevTools Performance panel to record a page load and inspect layout shift events. Each shift event shows the impacting element. Images without dimensions appear as the source of shift events with large impact fractions.
Lighthouse in DevTools, PageSpeed Insights, and the Lighthouse CI GitHub Action all report CLS and flag 'Image elements do not have explicit width and height' as a diagnostic item. Fix every flagged image and re-run Lighthouse to confirm the score improves.
# Check CLS with Lighthouse CLI
npx lighthouse https://yoursite.com --only-audits=cumulative-layout-shift,unsized-images
# Monitor CLS in field data
# CrUX API endpoint (replace with your URL):
https://chromeuxreport.googleapis.com/v1/records:queryRecord?key={API_KEY} Resources
Further reading
The prevent layout shift guide covers both the image attribute fix and placeholder URL patterns in depth. The LCP optimization guide covers the hero image loading side of Core Web Vitals.
https://fallback.pics/docs/
https://fallback.pics/placeholder-image-api/
https://fallback.pics/blog/prevent-layout-shift-missing-images/
https://fallback.pics/blog/lcp-optimization-failed-hero-images/ Key takeaways
What to standardize before shipping
- Images without width and height attributes reserve 0px height — add explicit attributes or aspect-ratio CSS to every img tag.
- A CLS score above 0.1 is 'Needs Improvement'; above 0.25 is 'Poor' — both affect Google Search Core Web Vitals status.
- Use placeholder URLs as the initial src for async-loaded content to prevent the shift when real images replace undefined src values.
- The CSS aspect-ratio property is a modern complement to attributes — use both for full coverage across old and new browsers.
- Measure CLS from images using Lighthouse's 'unsized-images' audit and the Performance panel layout shift events before and after fixes.
Production fallback layer
Use fallback.pics anywhere an image URL is accepted.
Start with one deterministic URL and standardize fallback behavior across your design system.