Mesrai
Back to blog
// essayTechnical Deep Dive

Image Component Without Width/Height: The CLS Score Mesrai Improves

Real PR catch: `<Image>` without dimensions — layout shift, CLS Web Vitals hit. Explicit width/height fix.

Mesrai TeamAugust 10, 20268 min read

An Avatar component using Next.js `<Image>`. Author left out width and height. Image loads, the page reflows, every other element shifts down by the avatar's height. CLS score craters.

The vulnerable diff

tsxcomponents/Avatar.tsx
// components/Avatar.tsx
import Image from "next/image";
export function Avatar({ src }: { src: string }) {
  // BUG: no width/height — image loads asynchronously, shifts layout
  return <Image src={src} alt="avatar" />;
}

What is wrong

Next.js's `<Image>` component (since 13.x) requires explicit dimensions to reserve space in the layout before the image loads. Without dimensions, the browser does not know how tall the slot will be — renders 0 px tall initially, then resizes to actual image height when the bytes arrive, pushing everything below down. This is Cumulative Layout Shift (CLS) — a Core Web Vitals metric that Google directly factors into search ranking.

The attack

Lighthouse audit shows the impact:

textexploit
Cumulative Layout Shift: 0.34 (Poor; target < 0.1)
   - Largest contributor: <Avatar> reflow on profile pages
   - Effective ranking penalty for the URL

Web Vitals are part of the Page Experience signal in Google search.
A poor CLS score on high-traffic pages directly costs traffic.

User-visible symptom: page renders, user starts to click the button, button moves away as image loads. Mis-clicks, frustration.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 30 sec ago

[mesrai] [code-review] [Performance] [CLS] [medium]

`<Image>` without width/height/fill. Browser cannot reserve space for
the image — when it loads, the layout shifts. CLS Web Vitals score
suffers.

Three correct shapes:

  // Known size:
  <Image src={src} alt="avatar" width={48} height={48} />

  // Responsive within a sized container (use fill):
  <div className="relative w-12 h-12">
    <Image src={src} alt="avatar" fill />
  </div>

  // Lazy + above-the-fold tuning:
  <Image src={src} alt="avatar" width={48} height={48}
         priority loading="eager" />

Always provide dimensions one way or another. The Image component
will not let you skip it intentionally — and that constraint exists
because of CLS.

The fix

tsxcomponents/Avatar.tsx (fixed)
// components/Avatar.tsx — fixed
import Image from "next/image";
export function Avatar({ src, size = 48 }: { src: string; size?: number }) {
  return <Image src={src} alt="avatar" width={size} height={size} />;
}

Explicit dimensions reserve space in the layout. Image bytes load into the reserved slot — no reflow. Layout shift score stays low. For responsive use cases where the parent decides the size, use `fill` with a positioned parent.

Why human review missed it

Image-shift CLS bugs are easy to miss because dev environments often have fast network — the image loads before the user can see the shift. Mesrai catches every Next.js `<Image>` without width/height/fill props.

Related rules + further reading

Mesrai rule pack: performance/next-image-dimensions — flags `<Image>` without width/height/fill.

web.dev: Cumulative Layout Shift.

Next.js docs: <Image> Component.

Takeaway

Width and height. Or `fill` in a positioned parent. CLS score depends on it. Mesrai catches every undimensioned Image.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.