How to Prevent Layout Shift from Missing Images
A practical performance guide for preventing image layout shift with dimensions, aspect ratios, placeholders, and stable fallback image URLs.
Missing images cause layout shift when the browser does not know how much space the image slot should occupy before the image loads or fails.
The fix is a layout contract: set dimensions or aspect ratio first, then swap the real image and fallback image inside the same reserved box.
Search intent
Why missing images cause layout shift
Image layout shift happens when content moves after the page has already painted. Missing dimensions are a common cause: the browser lays out nearby text, buttons, or cards, then has to recalculate when the image finally loads or fails.
A broken image fallback does not automatically fix CLS. If the fallback appears after the layout has already collapsed, users still see the page jump. The placeholder or fallback needs to live inside a reserved image slot from the start.
Slow image load
The image eventually appears, but the page had no reserved height while it was downloading.
Failed image URL
The real image never appears, and the browser broken-image state leaves an unstable or inconsistent slot.
Missing image src
The product, avatar, article, or CMS record has no media URL, so the component renders without a stable box.
Rule one
Always reserve the image box first
The most important rule is simple: reserve the final image space before fetching the image. Use width and height attributes on img elements when you know the intrinsic dimensions or intended ratio.
Modern browsers use width and height attributes to infer the aspect ratio early in layout. CSS can still make the image responsive while the attributes provide the sizing hint that prevents the initial jump.
<img
src="/media/dashboard-preview.jpg"
width="1200"
height="630"
alt="Dashboard preview"
style="max-width: 100%; height: auto;"
/> CSS
Use aspect-ratio when the container controls the size
Some layouts do not know the exact rendered pixel size because the image depends on the card width, viewport, grid track, or parent container. In those cases, use CSS aspect-ratio on the wrapper or image surface.
The key is to keep the real image and fallback image inside the same reserved box. The source can change, but the dimensions should not.
.media-slot {
aspect-ratio: 16 / 9;
width: 100%;
overflow: hidden;
background: #f4f4f5;
}
.media-slot > img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
} Fallback URL
Make the fallback match the same ratio
A fallback image should use the same dimensions or aspect ratio as the real image slot. If a 16:9 article image fails, use a 1200x630 or 1600x900 fallback. If a square product card fails, use a square fallback.
fallback.pics URLs make that easy because the dimensions are visible in the URL. The fallback source becomes part of the layout contract instead of an arbitrary replacement image.
// 16:9 article or social preview
https://fallback.pics/api/v1/1200x630/F4F4F5/18181B?text=Image+Unavailable
// Square product card
https://fallback.pics/api/v1/800x800/F4F4F5/18181B?text=Product+Image
// Avatar slot
https://fallback.pics/api/v1/avatar/96?text=User HTML
Prevent failed-image jumps with onerror
The HTML onerror pattern works when you need a direct fallback image without a framework. Keep the width and height on the original img element so the fallback replaces the source without changing the layout slot.
Set this.onerror to null before replacing the source so a failed fallback URL does not create a retry loop.
<img
src="/media/article-cover.jpg"
width="1200"
height="630"
alt="Article cover"
onerror="this.onerror=null;this.src='https://fallback.pics/api/v1/1200x630/F4F4F5/18181B?text=Image+Unavailable'"
/> React
Handle missing src before render and failed loads after render
A stable image component should handle two different states. If src is empty, render the fallback immediately. If src exists but fails to load, swap to the fallback while keeping the same width, height, or aspect-ratio wrapper.
This prevents a common bug where empty data renders no image slot at all, while failed network loads use a different code path.
import { useState } from 'react';
const fallbackSrc =
'https://fallback.pics/api/v1/1200x630/F4F4F5/18181B?text=Image+Unavailable';
export function StableImage({
src,
alt,
}: {
src?: string | null;
alt: string;
}) {
const initialSrc = src && src.trim() ? src : fallbackSrc;
const [currentSrc, setCurrentSrc] = useState(initialSrc);
return (
<img
src={currentSrc}
width="1200"
height="630"
alt={alt}
loading="lazy"
style={{ maxWidth: '100%', height: 'auto' }}
onError={() => {
if (currentSrc !== fallbackSrc) setCurrentSrc(fallbackSrc);
}}
/>
);
} Next.js
Use width and height or a sized fill container
In Next.js, use width and height when the image dimensions are known. For responsive card images that use fill, size the parent container with aspect-ratio, width, and position relative so the image has a stable box.
If your fallback is an SVG URL from fallback.pics, check your image optimization policy. Some teams use unoptimized for SVG fallback URLs, while others render a plain img for the fallback state.
// Known dimensions
<Image
src={src || fallbackSrc}
width={1200}
height={630}
alt="Article cover"
/>
// Fill mode requires a sized parent
<div className="media-slot">
<Image
src={src || fallbackSrc}
alt="Article cover"
fill
sizes="(min-width: 768px) 50vw, 100vw"
/>
</div> Skeletons
Separate loading placeholders from missing-image fallbacks
A skeleton placeholder is useful while the app is still fetching data or waiting for an image to load. A fallback image is useful after the app knows the image is missing or the image request failed.
Both can prevent layout shift if they occupy the same reserved image slot. The difference is the message: skeleton means wait, fallback means the expected media is unavailable.
Loading state
Use a skeleton or neutral placeholder while the data or image request is still pending.
Missing state
Use a labeled fallback such as Image Unavailable, Product Image, Article Image, or User.
Error state
Swap to the fallback inside the same slot after onerror, framework error state, or CDN failure detection.
Audit
How to find image CLS problems
Start with Lighthouse, PageSpeed Insights, or Chrome DevTools performance traces to identify layout shifts. Then inspect shifted image surfaces and look for missing width, missing height, missing aspect-ratio, or conditionally rendered image wrappers.
Do not only test the happy path. Test slow networks, empty image data, failed remote URLs, responsive breakpoints, product grids, article cards, avatars, hero images, and checkout or dashboard thumbnails.
Unsized img
The image has no width and height attributes and no stable CSS aspect ratio.
Conditional wrapper
The component renders no media box until image data exists.
Mismatched fallback
The fallback image has a different ratio from the real image slot.
Framework fill misuse
The image uses fill, but the parent container has no stable dimensions.
Privacy
Keep fallback URL labels public
Fallback image URLs can be logged by browsers, CDNs, analytics tools, error trackers, and support systems. Use generic labels in the text parameter.
Do not put secrets, tokens, email addresses, account IDs, order IDs, regulated data, customer names, private file names, or internal identifiers in placeholder URL text.
// Good
https://fallback.pics/api/v1/1200x630?text=Image+Unavailable
https://fallback.pics/api/v1/800x800?text=Product+Image
// Keep private values out of URL text Internal links
Where to go next
Use the broken image fallback page for production fallback behavior, then choose the stack-specific guide for HTML, React, or Next.js implementation details.
Use the skeleton placeholder generator when the state is still loading, and use static fallback URLs when the media is missing or has failed.
Broken image fallback: https://fallback.pics/broken-image-fallback/
Placeholder image API: https://fallback.pics/placeholder-image-api/
HTML guide: https://fallback.pics/guides/img-onerror-fallback/
React guide: https://fallback.pics/guides/react-image-fallback/
Next.js guide: https://fallback.pics/guides/nextjs-image-fallback/
Skeleton placeholder generator: https://fallback.pics/skeleton-placeholder-generator/
Product image placeholder: https://fallback.pics/product-image-placeholder/ Key takeaways
What to standardize before shipping
- Prevent image layout shift by reserving the image slot before the image loads or fails.
- Use width and height attributes when dimensions are known, and CSS aspect-ratio when the container controls the rendered size.
- Make fallback image URLs match the same dimensions or ratio as the real image slot.
- Handle empty src before render and failed image requests after render.
- Use skeletons for loading states and labeled fallback images for missing or failed media states.
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.