Cumulative Layout Shift (CLS): Voorkom omzetverlies door springerige layouts

Je gebruiker wil op "Koop nu" tikken, maar er laadt een banner die de knop naar beneden duwt. Ze tikken per ongeluk op "Annuleren". Dit gebeurt 800 keer per dag op jouw website. Je verliest omzet aan layout shifts.

Cumulative Layout Shift optimalisatie

Wat is CLS (en waarom het conversies om zeep helpt)?

CLS meet hoeveel de inhoud van je pagina verschuift tijdens het laden. Elke keer dat een afbeelding laat laadt, een banner verschijnt of weblettertypen worden gewisseld waardoor de layout verspringt, verliest de gebruiker het vertrouwen in je website.

Wat er gebeurt: De gebruiker begint je productbeschrijving te lezen. Een afbeelding laadt eindelijk en duwt alle tekst 300 pixels naar beneden. Ze zijn de draad kwijt. Ze zijn gefrustreerd. Ze gaan weg.

De CLS-score combineert hoeveel van het scherm werd beïnvloed (impact fraction) en hoe ver de content verschoof (distance fraction). Hogere score = frustrerender ervaring.

CLS-normen

Good0,1 of minder
Needs Improvement0,1 tot 0,25
PoorBoven 0,25

Belangrijk: Verschuivingen die door de gebruiker zelf worden veroorzaakt (bijvoorbeeld door op een knop te klikken die content toont) tellen niet mee. Alleen onverwachte verschuivingen tijdens het laden of terwijl de gebruiker probeert te interageren worden gemeten.

Waarom CLS belangrijk is voor de gebruikerservaring

Layout shifts veroorzaken echte problemen. Gebruikers tikken op de verkeerde knop als content verschuift. Ze verliezen hun leespositie als tekst verspringt. Ze haken af bij formulieren als invoervelden verschuiven tijdens het typen.

Onbedoelde klikken

De gebruiker wil op "Versturen" klikken, maar er laadt een advertentie die de knop naar beneden duwt. Ze klikken per ongeluk op "Annuleren". Op drukbezochte websites gebeurt dit duizenden keren per dag.

Verlies van leespositie

De gebruiker leest een artikel. Een afbeelding laadt laat en duwt de tekst naar beneden. Ze zijn de draad kwijt en moeten scrollen om hun plek terug te vinden.

Frustratie bij formulieren

De gebruiker begint te typen in een formulier. Een cookiebanner laadt en verschuift alles naar beneden. Ze typen nu in een ander veld zonder het te merken.

Websites met een slechte CLS zien 25% minder engagement en 15% hogere bouncepercentages. Visuele instabiliteit ondermijnt vertrouwen direct.

CLS meten

Velddata (echte gebruikers)

CLS verschilt sterk per gebruiker. Iemand met een snelle internetverbinding ziet content snel laden met minimale verschuivingen. Iemand op traag 3G ziet laat ladende content grote verschuivingen veroorzaken.

Labdata (testen)

Labtools laten potentiële CLS zien, maar missen echte situaties:

  • PageSpeed Insights — simuleert een mobiel apparaat met vertraagde verbinding
  • Lighthouse — ingebouwd in Chrome DevTools, geeft CLS-score en markeert verschuivende elementen
  • WebPageTest — visuele filmstrip toont exact wanneer verschuivingen optreden

CLS visueel debuggen

// Chrome DevTools Performance tab
// 1. Open DevTools > Performance
// 2. Start recording and reload the page
// 3. Look for "Layout Shifts" in the Experience track
// 4. Click on a shift to see which elements moved

CLS programmatisch meten

Installeer de web-vitals library:

npm install web-vitals

Track vervolgens CLS:

import {onCLS} from 'web-vitals';

onCLS((metric) => {
  console.log('CLS:', metric.value);

  // Get details about each shift
  metric.entries.forEach(entry => {
    console.log('Layout shift:', {
      value: entry.value,
      startTime: entry.startTime,
      // Elements that shifted
      sources: entry.sources?.map(source => ({
        node: source.node,
        previousRect: source.previousRect,
        currentRect: source.currentRect
      }))
    });
  });
});

Veelvoorkomende oorzaken van CLS

1. Afbeeldingen zonder afmetingen

Wanneer afbeeldingen laden, weet de browser niet hoe hoog ze zijn. Hij reserveert 0 px hoogte en verschuift de layout zodra de afbeelding laadt.

Fix: Geef altijd width- en height-attributen op bij afbeeldingen.

2. Advertenties zonder gereserveerde ruimte

Advertentienetwerken laden dynamisch. Als je geen ruimte reserveert voor de advertentie, verschuift de content naar beneden zodra deze laadt. Dit is de grootste oorzaak van slechte CLS op content-websites.

Fix: Reserveer vaste containers voor advertenties met min-height.

3. Weblettertypen die tekst laten verschuiven

Lettertypen laden laat. De browser toont eerst terugvaltekst en wisselt dan naar het echte lettertype. Als de lettertypen verschillende afmetingen hebben, verschuift de tekst en daarmee de layout.

Fix: Gebruik font-display: optional of size-adjust om fallback-lettertypen te laten aansluiten.

4. Dynamisch ingevoegde content

Banners, meldingen en cookiemeldingen die content naar beneden duwen zodra ze verschijnen. Gebruikers beginnen te lezen en dan springt alles weg.

Fix: Laad kritieke UI-elementen tijdens de initiële render, of overlay ze in plaats van content te verschuiven.

5. Animaties die layout triggeren

Het animeren van eigenschappen zoals width, height, top en left dwingt de browser tot herberekening van de layout en kan verschuivingen in omliggende content veroorzaken.

Fix: Gebruik transform en opacity voor animaties (die triggeren geen layout).

CLS-optimalisatietechnieken

1. Geef altijd afbeeldingsafmetingen op

Stel width- en height-attributen in

<!-- Bad: No dimensions specified -->
<img src="hero.jpg" alt="Hero">

<!-- Good: Explicit dimensions -->
<img
  src="hero.jpg"
  alt="Hero"
  width="1200"
  height="600"
/>

<!-- Modern browsers calculate aspect ratio automatically:
     aspect-ratio = 1200 / 600 = 2
     The image will scale responsively while maintaining space -->

Gebruik CSS aspect-ratio voor responsieve afbeeldingen

<!-- For images with srcset/sizes -->
<img
  srcset="hero-400.jpg 400w,
          hero-800.jpg 800w,
          hero-1200.jpg 1200w"
  sizes="(max-width: 768px) 100vw, 1200px"
  src="hero-1200.jpg"
  alt="Hero"
  width="1200"
  height="600"
  style="width: 100%; height: auto;"
/>

/* CSS solution for unknown dimensions */
.image-container {
  aspect-ratio: 16 / 9;
  width: 100%;
  overflow: hidden;
}

.image-container img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Impact: Elimineert afbeelding-gerelateerde CLS volledig (kan CLS met 0,1–0,3 verlagen)

2. Reserveer ruimte voor advertenties en embeds

Vaste containers voor advertenties

<!-- Reserve space for ad before it loads -->
<div class="ad-container">
  <div id="ad-slot"></div>
</div>

<style>
.ad-container {
  min-height: 250px; /* Standard ad height */
  width: 100%;
  background: #f3f4f6; /* Placeholder color */
  display: flex;
  align-items: center;
  justify-content: center;
}

.ad-container::before {
  content: 'Advertisement';
  color: #9ca3af;
  font-size: 0.75rem;
  text-transform: uppercase;
}
</style>

Ruimte reserveren voor embeds

<!-- YouTube embed with aspect ratio -->
<div class="video-container">
  <iframe
    src="https://www.youtube.com/embed/dQw4w9WgXcQ"
    title="Video"
    frameborder="0"
    allow="accelerometer; autoplay; encrypted-media; gyroscope"
    allowfullscreen
  ></iframe>
</div>

<style>
.video-container {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 aspect ratio */
  height: 0;
  overflow: hidden;
}

.video-container iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>

3. Lettertype-laden optimaliseren

Fallback-lettertype laten aansluiten op weblettertype

/* Use size-adjust to match fallback font to web font */
@font-face {
  font-family: 'Roboto';
  src: url('/fonts/roboto.woff2') format('woff2');
  font-display: swap;
}

@font-face {
  font-family: 'Roboto Fallback';
  src: local('Arial');
  /* Adjust fallback to match Roboto's metrics */
  size-adjust: 95.7%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

body {
  font-family: 'Roboto', 'Roboto Fallback', Arial, sans-serif;
}

/* Or use font-display: optional to prevent swap entirely */
@font-face {
  font-family: 'Roboto';
  src: url('/fonts/roboto.woff2') format('woff2');
  font-display: optional; /* Only use if loaded in ~100ms */
}

Tool: Gebruik de Fallback Font Generator om size-adjust-waarden te berekenen

4. Voorkom het invoegen van content boven bestaande content

Gebruik fixed/sticky positionering voor banners

/* Bad: Pushes content down */
.cookie-banner {
  position: relative;
  width: 100%;
  background: #1a1a1a;
  color: white;
  padding: 1rem;
}

/* Good: Overlays content */
.cookie-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: #1a1a1a;
  color: white;
  padding: 1rem;
  z-index: 1000;
  /* No layout shift */
}

/* Better: Reserve space in advance if banner is critical */
body {
  padding-bottom: 80px; /* Height of banner */
}

.cookie-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 80px;
}

5. Vermijd animaties die layout triggeren

/* Bad: These trigger layout recalculation */
.box {
  transition: width 0.3s, height 0.3s, top 0.3s, left 0.3s;
}

/* Good: These are GPU-accelerated and don't affect layout */
.box {
  transition: transform 0.3s, opacity 0.3s;
}

/* Example: Slide-in animation */
/* Bad */
@keyframes slideIn {
  from { left: -300px; }
  to { left: 0; }
}

/* Good */
@keyframes slideIn {
  from { transform: translateX(-300px); }
  to { transform: translateX(0); }
}

Geavanceerde CLS-optimalisatiestrategieën

Kritieke lettertypen preloaden

<!-- Preload the most critical font file -->
<link
  rel="preload"
  href="/fonts/roboto-regular.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

<!-- Only preload 1-2 critical fonts
     Loading too many delays LCP -->

Skeleton screens voor dynamische content

<!-- Show placeholder while content loads -->
<div class="article-skeleton">
  <div class="skeleton-image"></div>
  <div class="skeleton-title"></div>
  <div class="skeleton-text"></div>
  <div class="skeleton-text"></div>
</div>

<style>
.skeleton-image {
  width: 100%;
  height: 300px;
  background: linear-gradient(
    90deg,
    #f0f0f0 25%,
    #e0e0e0 50%,
    #f0f0f0 75%
  );
  background-size: 200% 100%;
  animation: loading 1.5s infinite;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
</style>

De CSS contain-eigenschap gebruiken

/* Tell browser this element won't affect outside layout */
.article-card {
  contain: layout style paint;
  /* Browser can optimise rendering and prevent shifts */
}

.sidebar-widget {
  contain: size layout style paint;
  /* Fixed size, fully isolated */
  width: 300px;
  height: 400px;
}

CLS monitoren over tijd

CLS kan plotseling verslechteren wanneer je nieuwe features deployt. Stel monitoring in om regressies vroeg te signaleren.

Verschuivende elementen tracken

import {onCLS} from 'web-vitals';

onCLS((metric) => {
  // Only report poor CLS (> 0.1)
  if (metric.value > 0.1) {
    // Get the largest shift
    const largestShift = metric.entries.reduce((max, entry) =>
      entry.value > max.value ? entry : max
    );

    // Identify the element that shifted most
    const shiftingElement = largestShift.sources?.[0];

    analytics.track('poor_cls', {
      cls_value: metric.value,
      largest_shift_value: largestShift.value,
      element_selector: getSelector(shiftingElement?.node),
      shift_time: largestShift.startTime,
      page_url: location.pathname
    });
  }
});

function getSelector(node) {
  if (!node) return 'unknown';
  if (node.id) return `#${node.id}`;
  if (node.className) return `.${node.className.split(' ')[0]}`;
  return node.tagName.toLowerCase();
}

Alerts instellen

  • CrUX API: Bewaak je 75e-percentiel CLS wekelijks
  • RUM-drempelwaartealarmen: Ontvang een melding als CLS boven de 0,1 uitkomt voor meer dan 10% van de gebruikers
  • Regressiedetectie: Alert bij een CLS-stijging van meer dan 0,05 na een deployment

Performance budgets

  • CLS: Onder 0,1 (75e percentiel)
  • Afbeeldingen: Alle afbeeldingen moeten width en height hebben
  • Lettertypen: Maximaal 4 lettertype-bestanden (2 gewichten × 2 stijlen), gebruik font-display: optional
  • Advertenties: Alle advertentie-slots moeten gereserveerde ruimte hebben

CLS-optimalisatiechecklist

Hulp nodig bij het oplossen van layout shifts?

CLS-problemen kunnen subtiel zijn en lastig reproduceerbaar. Wij kunnen je pagina's auditeren, alle verschuivende elementen identificeren en uitgebreide fixes implementeren. Neem contact op of voer een gratis benchmark uit om je huidige CLS te zien.

Hulp nodig bij het oplossen van je CLS?

Wij auditen je layout-issues en implementeren fixes die onverwachte verschuivingen volledig elimineren.