Back to blog

Published Updated

How to Reduce CLS in Next.js Websites

By Tushar ChoudharyCLS • "Next.js • "Core Web Vitals • "Images • "Fonts • "2026

Fix CLS in Next.js by reserving space for images, fonts, embeds, banners, async content, and animations, then validate changes with lab and field data.

How to Reduce CLS in Next.js Websites

Cumulative Layout Shift (CLS) measures unexpected visual movement. A user may try to tap a button, only for an image, banner, font, form error, or late component to move the target. In a Next.js site, the cause is usually not the framework itself; it is missing size information, unstable content insertion, font metric differences, or animation of layout-changing properties.

The correct fix is to identify the element that moved and the element that caused it, reserve the final geometry, and verify the same user journey after deployment.

Quick Answer

Give images and media an intrinsic size or stable aspect-ratio container, use next/image correctly, load fonts through next/font with compatible fallbacks, reserve space for embeds and async blocks, keep banners from being inserted above existing content, render form errors in planned regions, and animate transform/opacity rather than dimensions or position where possible. Validate with Chrome DevTools/Lighthouse and field data because lab and real-user CLS can differ.

For a broader performance review, use website speed optimisation or web application development.

Understand the Evidence

According to web.dev's CLS guidance, CLS is based on unexpected layout-shift scores during the page lifecycle. User-initiated movement within the allowed interaction window may be treated differently, so reproduce the actual journey rather than assuming every visual change is counted.

Use three sources:

EvidenceWhat it helps findLimitation
PageSpeed/CrUX field dataReal-user origin or URL experienceAggregated and delayed; may not identify element
LighthouseRepeatable initial-load lab testDoes not cover every interaction or device
DevTools Performance/Layout ShiftsSpecific moving elements and timingRequires a reproducible session

Record viewport, network/device profile, consent state, login state, route, and actions. A cookie banner, carousel, validation error, or client-only widget may appear only in a particular path.

Fix 1: Size Every Image

The official Next.js Image documentation explains that width and height provide the intrinsic aspect ratio used to reserve space; remote images require dimensions because Next.js cannot inspect them at build time.

import Image from "next/image";

<Image
  src="/team/tushar.webp"
  alt="VASUYASHII founder reviewing a software workflow"
  width={800}
  height={600}
  sizes="(max-width: 768px) 100vw, 50vw"
  style={{ width: "100%", height: "auto" }}
/>

width and height do not force that rendered size when CSS makes the image responsive; they establish the ratio. The sizes prop also helps the browser select an appropriate responsive source.

For fill, the parent must have a stable size and appropriate positioning:

<div className="relative aspect-[4/3] overflow-hidden">
  <Image
    fill
    src={photoUrl}
    alt={photoAlt}
    sizes="(max-width: 768px) 100vw, 33vw"
    className="object-cover"
  />
</div>

Do not calculate the container height only after the image loads.

Fix 2: Reserve Space for CMS and Remote Media

If a CMS provides an image URL but not its dimensions, extend the content model to store width, height, or aspect ratio. For legacy content, generate metadata during ingestion or use a known ratio container with a deliberate crop.

Avoid a temporary zero-height wrapper. A blur placeholder improves perceived loading but does not replace dimensions.

Fix 3: Stabilise Web Fonts

Font swapping can change line breaks and element height when fallback and final fonts have different metrics. The Next.js Font documentation describes self-hosted optimisation and fallback adjustment through next/font.

import { Inter } from "next/font/google";

const inter = Inter({
  subsets: ["latin"],
  display: "swap",
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

Load only required families, weights, subsets, and axes. Apply the font class at a stable scope, and test long headings at mobile widths. If using a local font, provide accurate files and fallback behaviour.

Do not hide all text until a font arrives; that trades stability for delayed content.

Fix 4: Give Embeds a Placeholder Geometry

Videos, maps, booking widgets, chat tools, forms, advertisements, and social embeds often resize after JavaScript loads. Wrap them in a container matching the final dimensions.

.video-frame {
  aspect-ratio: 16 / 9;
  width: 100%;
  background: #f1f5f9;
  overflow: hidden;
}

.video-frame iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

If the final height varies, reserve a conservative minimum and allow content to grow below rather than pushing already visible content unexpectedly.

Fix 5: Do Not Insert Banners Above Content

Cookie notices, app-install prompts, discount bars, login warnings, and location prompts can move the header and hero after hydration. Choose one of these patterns:

  • render the banner server-side when its state is known;
  • reserve its slot from the first paint;
  • use a fixed overlay that does not change document flow;
  • place new status content below the current focus rather than above it.

Respect accessibility: overlays must not cover required controls, trap focus incorrectly, or make content unreadable.

Fix 6: Plan Async and Personalised Content

Client-side user state can replace a short skeleton with a tall account panel. Search suggestions, prices, reviews, related posts, and dashboards can do the same.

The placeholder and loaded component should share stable dimensions for the tested breakpoint. If content can vary materially, use a minimum-height region or place additional results below existing content.

Avoid rendering nothing on the server and inserting the entire above-the-fold component after mount unless there is a genuine client-only requirement.

Fix 7: Stabilise Form Validation

Inline error messages often push fields and the submit button after a user submits. Reserve an error region, keep labels stable, and associate messages with inputs.

.field-message {
  min-height: 1.25rem;
}

For a general form-level error, place a planned alert block near the form heading and move focus deliberately. Do not inject it above the page header.

Fix 8: Use Composited Animation Properties

Animating height, width, top, left, margins, or background position can trigger layout or paint work and may create unstable movement. Prefer transform and opacity for decorative motion.

For an accordion, user-initiated expansion is expected, but surrounding content still needs coherent behaviour. Do not run automatic expanding/collapsing sections above the user's current reading position.

Respect prefers-reduced-motion and avoid continuous motion that competes with content.

Fix 9: Make Carousels and Testimonials Stable

Slides with different text lengths can resize the carousel and shift following sections. Use a stable slide height at each breakpoint, constrain excerpts, or allow the carousel to grow to the tallest measured content before interaction.

Do not read layout measurements and write styles repeatedly during animation. Batch reads/writes or use CSS layout where possible.

Fix 10: Watch Hydration and Responsive Branches

Rendering one layout on the server and replacing it with another after checking window.innerWidth can cause a shift. Prefer responsive CSS. When JavaScript branching is necessary, make the server fallback geometrically compatible and avoid changing the main heading or navigation height after hydration.

Also investigate invalid HTML and hydration warnings; recovery can replace unexpected DOM.

A Practical Next.js Audit Sequence

  1. Test the production URL in mobile and desktop conditions.
  2. Record initial load and common interactions in DevTools Performance.
  3. Inspect Layout Shift entries and affected nodes.
  4. Identify the source: missing geometry, inserted content, font, animation, or hydration.
  5. Add a stable layout contract rather than a timeout or visual patch.
  6. Retest the same viewport, state, and interaction.
  7. Verify keyboard use and reduced-motion behaviour.
  8. Deploy and monitor field data over its normal collection window.

Our implementation review captures before/after videos or screenshots, the responsible DOM region, the CSS/component change, and repeated mobile/desktop measurements. This first-party evidence prevents a single favourable Lighthouse run from being treated as a complete result.

CLS Fix Matrix

CauseWeak patchDurable fix
Unsized imageHide until loadedIntrinsic dimensions/aspect ratio
Remote CMS imageGuess height after requestStore dimensions in content model
Font swapDelay all textnext/font, limited variants, compatible fallback
Async widgetAdd random min-heightContract based on actual component states
BannerInsert at top after mountServer render, reserve slot, or overlay
Form errorPush every fieldReserved accessible message region
CarouselMeasure on every slideStable breakpoint height/content constraint
AnimationAnimate height/topTransform/opacity where appropriate
Responsive JSReplace desktop DOM after mountCSS-driven responsive layout

What Not to Do

  • Do not set a large arbitrary min-height across the whole page.
  • Do not remove meaningful content solely to improve one lab score.
  • Do not use priority or eager loading on every image; loading priority addresses different performance concerns.
  • Do not assume next/image fixes a parent with no stable dimensions.
  • Do not ignore authenticated, cookie-consent, or post-interaction states.
  • Do not animate layout continuously above important content.
  • Do not optimise only desktop when traffic is mainly mobile.
  • Do not declare success from one Lighthouse run.

Release Checklist

If the shift comes from a shared layout or client-side component used across routes, include it in a focused software development remediation scope instead of applying unrelated patches page by page.

  • [ ] Main images have dimensions or a stable aspect-ratio container.
  • [ ] Remote/CMS image metadata is available.
  • [ ] Responsive sizes reflects real layout widths.
  • [ ] Fonts are loaded through a controlled Next.js strategy.
  • [ ] Embeds, maps, and widgets reserve their final region.
  • [ ] Consent and announcement banners do not push rendered content.
  • [ ] Skeleton and loaded states are geometrically compatible.
  • [ ] Form errors have reserved accessible locations.
  • [ ] Motion uses stable properties and reduced-motion support.
  • [ ] Mobile/desktop initial load and interactions were recorded.
  • [ ] Field monitoring is scheduled after deployment.

FAQs

What CLS score should we target?

Use the current Core Web Vitals guidance in PageSpeed/web.dev and evaluate the 75th percentile field experience when available. More importantly, identify and remove unexpected movement in critical journeys.

Does next/image automatically prevent CLS?

It helps when used with intrinsic dimensions or a correctly sized fill parent. An unstable parent, late conditional rendering, or surrounding content can still shift.

Can fonts create CLS even with font-display: swap?

Yes. A fallback with different metrics can change line wrapping when the final font appears. next/font, fallback adjustment, limited variants, and layout testing reduce the risk.

Why is Lighthouse CLS zero while Search Console reports an issue?

The lab run covers one controlled page load. Field users may see different devices, consent states, interactions, dynamic content, or long sessions. Use both data types.

Should every dynamic section have a fixed height?

No. It should have a predictable layout contract. Fixed height may clip content; aspect ratio, minimum height, skeleton matching, or insertion below existing content may be more appropriate.

Can VASUYASHII investigate CLS?

Contact VASUYASHII with affected URLs, PageSpeed/Search Console evidence, device/state where movement occurs, and any recent component or font changes.

Final Decision

Fix CLS at the component that lacks a stable geometry contract. Reserve the space, render predictable states, validate the exact user journey, and then wait for field data to confirm the production effect.