Blog Performance 11 min read

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.

Image layout shiftCLS imagesCore Web VitalsImage placeholders
How to Prevent Layout Shift from Missing Images

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.

Implementation text
<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.

Implementation text
.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.

Implementation text
// 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.

Implementation text
<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.

Implementation tsx
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.

Implementation text
// 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.

Implementation 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.

Implementation text
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.

Read the docs