vibecheck0.3.0
Performancelayout-thrashingwarning

Cumulative layout shift (CLS) in Next.js

Layout shift is when content jumps after it has already rendered — an image loads and pushes the text down, a banner injects itself above the fold, a web font swaps and reflows everything. It is a Core Web Vitals metric Google ranks on, and it makes users tap the wrong thing. AI-built pages shift constantly because generated markup omits width/height on images and drops late content in without reserving space.

Symptoms

  • Text and buttons jump down as images or ads load in
  • You tap one thing and hit another because it moved
  • A visible reflow when the web font swaps in
  • PageSpeed Insights reports a poor CLS score (> 0.1)

How VibeCheck catches it

In your widget · Problems

warninglayout-thrashingLayout shift cluster detected (3 shifts)

To your coding agent · MCP

agent › get_detected_issues
{ detector: "layout-thrashing", issue: "Layout shift cluster detected (3 shifts)", threshold: "≥ 3 layout shifts within a 500ms window, with no recent user input" }

The same string in your widget and in your agent’s context — no screenshot, no copy-paste.

Root causes

  • Images and videos without width/height (or an aspect-ratio box) reserving space
  • Content injected above existing content (banners, cookie bars, ads) after load
  • Web fonts swapping and reflowing text (FOUT) without a matched fallback
  • Dynamically sized containers that resize once data arrives

The fix for Next.js

next/image reserves space from the width/height (or fill + a sized parent) automatically, and next/font eliminates font-swap shift by self-hosting with a size-adjusted fallback. Using both removes the two most common CLS sources.

tsx
import Image from 'next/image'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] }) // no layout shift on swap

<Image src="/hero.jpg" width={1200} height={630} alt="Hero" />

Next.js docs →

Steps

  1. Add width and height (or aspect-ratio) to every image, video, and iframe
  2. Reserve a fixed-size slot for anything injected after load (ads, banners, embeds)
  3. Use font-display: optional or a size-adjusted fallback to avoid font-swap reflow

See the general, framework-agnostic fix →

FAQ

What CLS score do I need?
Google considers CLS good at 0.1 or below, and poor above 0.25, for 75% of page loads. It is one of the three Core Web Vitals used in ranking.
Why does the layout-thrashing detector flag this?
VibeCheck observes the browser’s layout-shift entries. When three or more fire within 500ms without user input, it flags a cluster — that burst is exactly what a jumpy load feels like.
My image is responsive — do I still set width and height?
Yes. Set the intrinsic width/height attributes so the browser knows the aspect ratio, then use CSS (width:100%; height:auto) to scale it. That gives you responsiveness with zero shift.