Largest Contentful Paint (LCP): Voorkom omzetverlies door trage hero-afbeeldingen

Je hero-afbeelding laadt in 4,5 seconden. Gebruikers wachten twee seconden, gaan ervan uit dat je website stuk is en vertrekken. Je verliest de sale voordat ze zelfs maar hebben gezien wat je aanbiedt.

Largest Contentful Paint optimalisatie

Wat is LCP (en waarom zou je er om geven)?

LCP meet wanneer de hoofdinhoud van je pagina zichtbaar wordt. Niet wanneer de hele pagina klaar is met laden, maar wanneer het element dat er het meest toe doet verschijnt.

Op de meeste websites is dat:

  • De hero-afbeelding op de homepage
  • Productafbeeldingen op categorie- of detailpagina's
  • De headerafbeelding bij blogartikelen
  • Grote tekstblokken als je geen afbeeldingen gebruikt

Het probleem: Als je LCP-element traag laadt, denken gebruikers dat de hele website stuk is. Ze zien geen laadspinner en beseffen niet dat JavaScript nog aan het downloaden is. Ze zien gewoon een leeg scherm en vertrekken.

Goede LCP-norm

GoodOnder 2,5 s
Needs Improvement2,5 s – 4,0 s
PoorBoven 4,0 s

De 2,5-secondenregel: Google stelt "goed" LCP op 2,5 seconden, omdat gebruikers vanaf dat moment gaan twijfelen of je website wel werkt. Haal je die grens, dan zit je in het groen. Haal je hem niet, dan verlies je bezoekers.

De commerciële kosten van een trage LCP

Een trage hero-afbeelding is niet alleen een technisch probleem. Het is een omzetprobleem. Zodra je LCP boven de 2,5 seconden uitkomt:

53% bounce op mobiel

Onderzoek van Google: als je mobiele pagina langer dan 3 seconden laadt, verlaat meer dan de helft van je bezoekers de pagina direct.

7% conversieverlies per seconde

Elke seconde vertraging in je LCP-element kost je 7% conversie. Een LCP van 4 seconden betekent 10,5% minder potentiële omzet.

Lagere zoekposities

LCP is een rankingfactor. Slechte scores zorgen dat je lager in de zoekresultaten belandt, wat je organisch verkeer kost.

Praktijkvoorbeeld: Een e-commercewebsite die wij auditeerden had een LCP van 4,8 seconden op productpagina's. Uit de analytics bleek dat 61% van de mobiele gebruikers binnen 5 seconden vertrok. We reduceerden de LCP naar 1,9 seconden. Het bouncepercentage daalde naar 34%. Dezelfde producten, dezelfde prijzen — alleen snellere afbeeldingen.

LCP meten (en welk getal je kunt vertrouwen)

LCP kun je op twee manieren meten: in het lab (synthetisch testen met tools) en in het veld (echte gebruikers op echte apparaten). Beide vertellen je iets anders.

Velddata: wat echte gebruikers ervaren

Dit zijn de cijfers die tellen voor rankings en omzet. Gebruik hiervoor:

  • Iron/Out gratis benchmark — haal je LCP op uit het Chrome UX Report (28-daags gemiddelde van echte gebruikers)
  • Google Search Console — het Core Web Vitals-rapport toont welke pagina's slecht scoren en waarom
  • Real User Monitoring (RUM) — volg LCP voor elke bezoeker met onze observability-inrichting

Vertrouw velddata voor zakelijke beslissingen. Labtests laten zien wat er kan in ideale omstandigheden. Velddata toont wat je daadwerkelijke klanten ervaren op hun eigen apparaten en verbindingen. Dat is wat bouncepercentage en conversie beïnvloedt.

Labdata: voor ontwikkeling en debugging

Nuttig voor het testen van wijzigingen vóór deployment:

  • PageSpeed Insights — snelle test van elke URL (kijk eerst naar velddata, dan naar labdata)
  • Chrome DevTools — het Performance-paneel toont precies welk element je LCP is
  • Lighthouse — ingebouwd in Chrome, geeft een score en aanbevelingen

LCP meten met JavaScript

Installeer eerst de web-vitals library van Google:

npm install web-vitals

Gebruik vervolgens deze code om LCP te tracken:

// Track LCP with Web Vitals library
import {onLCP} from 'web-vitals';

onLCP((metric) => {
  console.log('LCP:', metric.value, 'ms');
  console.log('LCP element:', metric.entries[0].element);

  // Send to analytics
  gtag('event', 'web_vitals', {
    name: 'LCP',
    value: metric.value,
    metric_id: metric.id
  });
});

De web-vitals library is de officiële JavaScript-bibliotheek van Google voor het meten van Core Web Vitals. Deze handelt alle complexiteit van browser-API's voor je af.

LCP-onderdelen begrijpen (voor debugging)

Je LCP-score bestaat eigenlijk uit vier afzonderlijke tijdmetingen bij elkaar opgeteld. Als je een trage LCP debugt, vertelt het weten welk onderdeel de bottleneck is precies waar je moet ingrijpen.

LCP breakdown (4 onderdelen):

1. TTFB               → Serverresponstijd
2. Resource load delay → Hoe lang voordat de browser de afbeelding opvraagt
3. Resource load time  → Downloadduur van de afbeelding
4. Render delay       → Tijd van download tot paint

Hoe je dit gebruikt: Chrome DevTools toont de LCP-uitsplitsing in het Performance-tabblad onder "Insights". Kijk welk onderdeel het grootst is. Dat is je bottleneck.

Als TTFB het grootst is (boven 800 ms)

Je server of CDN is traag. De browser kan pas beginnen met het downloaden van de afbeelding als de HTML binnenkomt. Los eerst je backend op.

Als resource load delay het grootst is (boven 500 ms)

De browser ontdekte je afbeelding te laat. Waarschijnlijk omdat het een CSS-achtergrondafbeelding is, je lazy loading hebt ingesteld, of render-blocking scripts de parsing hebben vertraagd. Gebruik <link rel="preload"> of zet de afbeelding direct in de HTML.

Als resource load time het grootst is (boven 1000 ms)

Je afbeeldingsbestand is te groot of het netwerk is traag. Comprimeer de afbeelding, gebruik WebP/AVIF en serveer kleinere formaten aan mobiele gebruikers. Dit is het meest voorkomende probleem (73% van de LCP-elementen zijn afbeeldingen).

Als render delay het grootst is (boven 200 ms)

Iets blokkeert de browser bij het renderen nadat de afbeelding gedownload is. Meestal A/B-testscripts die de pagina verbergen, of zware JavaScript die de main thread blokkeert. Controleer wat er draait op het moment dat de afbeelding klaar is met laden.

Pro-tip: Bij de meeste websites is resource load time het grootste onderdeel. Begin met het optimaliseren van je afbeeldingen. Als dat het probleem niet oplost, graaf dan verder in de andere onderdelen.

Waarom je LCP traag is (de gebruikelijke verdachten)

We hebben honderden websites geauditeerd. Dit zijn de problemen die steeds terugkomen:

1. Enorme, niet-geoptimaliseerde afbeeldingen

Je designer exporteerde een hero-afbeelding van 3,2 MB als PNG op 4000 pixels breed. Mobiele gebruikers op 4G downloaden een bestand dat 10 keer groter is dan nodig. Dit is veruit de meest voorkomende oorzaak van trage LCP die wij tegenkomen.

Fix: Gebruik moderne formaten (WebP of AVIF), comprimeer agressief en serveer passend formaat voor elk scherm.

2. Trage serverrespons (TTFB boven 800 ms)

Als je server 2 seconden nodig heeft om te reageren, kan de browser nog niet eens beginnen met het downloaden van je afbeelding. De pagina blijft leeg terwijl de server databasequeries verwerkt of wacht op externe API's.

Fix: Gebruik een CDN om pagina's op de edge te cachen, optimaliseer databasequeries, of schakel over op Cloudflare met goede cachingconfiguratie.

3. Render-blocking CSS en JavaScript

Je browser downloadt 200 KB aan CSS voordat de pagina überhaupt kan beginnen met renderen. Ondertussen blokkeren analytics-scripts en A/B-testtools de main thread.

Fix: Houd kritieke CSS klein (onder 50 KB), stel niet-essentiële stijlen uit en laad scripts van derden asynchroon.

4. Client-side rendering met React/Vue

Je gebruikt een modern JavaScript-framework, maar al je pagina-inhoud wordt client-side gerenderd. Gebruikers zien een leeg scherm totdat React gedownload (300 KB), geparsed, uitgevoerd is, data van een API heeft opgehaald en uiteindelijk de hero-sectie rendert.

Fix: Server-side rendering (SSR) of static site generation (SSG). Next.js en Gatsby doen dit automatisch.

5. Lazy loading op de hero-afbeelding

Iemand heeft loading="lazy" op alle afbeeldingen gezet, inclusief de hero. Met lazy loading stelt de browser het downloaden van de afbeelding uit totdat deze in de buurt van de viewport is — maar voor above-the-fold content zorgt dit juist voor overbodige vertraging. Dit kan 500–800 ms extra kosten.

Fix: Gebruik nooit lazy loading op above-the-fold afbeeldingen. Gebruik loading='eager' of laat het attribuut weg.

Hoe je een trage LCP oplost (gerangschikt op impact)

1. Begin met je afbeeldingen

Afbeeldingen veroorzaken 90% van alle LCP-problemen. Begin hier. Al het andere is bijzaak.

Overstappen op moderne formaten (30–50% kleinere bestanden)

<!-- Use WebP or AVIF instead of JPEG/PNG -->
<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Hero" width="1200" height="600">
</picture>

<!-- Result: 800KB JPEG → 240KB WebP → 180KB AVIF -->

WebP wordt ondersteund door 95% van alle browsers. AVIF is nieuwer en nog beter, maar gebruik WebP als fallback voor oudere browsers.

Serveer het juiste formaat per apparaat (60–80% reductie op mobiel)

<!-- Don't send a 3000px image to a 400px phone screen -->
<img
  srcset="
    hero-400.jpg 400w,
    hero-800.jpg 800w,
    hero-1200.jpg 1200w
  "
  sizes="(max-width: 768px) 100vw, 1200px"
  src="hero-800.jpg"
  alt="Hero"
  width="1200"
  height="600"
/>

<!-- Mobile gets 400px image (80KB), desktop gets 1200px (320KB) -->

De meeste websites sturen volledige desktopresolutie naar mobiele gebruikers. Dat is verspilling en traag.

LCP-afbeelding preloaden

<!-- Schedule hero image for high-priority early fetch -->
<link
  rel="preload"
  as="image"
  href="hero.jpg"
  imagesrcset="
    hero-400.jpg 400w,
    hero-800.jpg 800w,
    hero-1200.jpg 1200w
  "
  imagesizes="(max-width: 768px) 100vw, 1200px"
/>

<!-- Note: preload schedules the fetch with higher priority,
     but doesn't guarantee immediate download. The browser
     still prioritises based on available bandwidth. -->

Impact: 0,2–0,8 s LCP-verbetering (helpt de browser de afbeelding eerder te ontdekken)

2. Verminder de serverresponstijd

  • Gebruik een CDN: Serveer content vanuit locaties dicht bij je gebruikers
  • Implementeer caching: Cache pagina's op de edge met Cloudflare
  • Optimaliseer databasequeries: Gebruik indexen, connection pooling en queryoptimalisatie
  • Upgrade je hosting: Stap over op snellere servers als je op gedeelde hosting zit

Doel: Time to First Byte (TTFB) onder 800 ms

3. Elimineer render-blocking resources

Niet-kritieke JavaScript uitstellen

<!-- Load analytics and chat widgets after page load -->
<script src="analytics.js" defer></script>
<script src="chat-widget.js" async></script>

<!-- For React/Next.js, use dynamic imports -->
const ChatWidget = dynamic(() => import('./ChatWidget'), {
  ssr: false,
  loading: () => null
});

4. Gebruik server-side rendering

Als je React, Vue of een vergelijkbaar framework gebruikt:

  • Next.js: Gebruik getStaticProps of getServerSideProps
  • Gatsby: Doet dit automatisch
  • Nuxt: Gebruik asyncData of fetch

SSR zorgt ervoor dat gebruikers direct content zien, nog voordat JavaScript is geladen.

Geavanceerde LCP-optimalisatiestrategieën

Priority Hints

<!-- Tell browser this image is high priority -->
<img
  src="hero.jpg"
  alt="Hero"
  fetchpriority="high"
  width="1200"
  height="600"
/>

<!-- Deprioritize below-the-fold images -->
<img
  src="footer-logo.jpg"
  alt="Logo"
  fetchpriority="low"
  loading="lazy"
/>

Early Hints (HTTP 103)

Vertel browsers alvast welke resources ze moeten downloaden terwijl de server de pagina nog aan het genereren is:

// Server sends 103 Early Hints before full response
HTTP/1.1 103 Early Hints
Link: </hero.jpg>; rel=preload; as=image
Link: </styles.css>; rel=preload; as=style

// Then sends actual page
HTTP/1.1 200 OK
Content-Type: text/html
...

Ondersteund door Cloudflare en moderne CDN's.

Service Workers voor terugkerende bezoekers

// Cache LCP image for instant repeat visits
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll([
        '/',
        '/hero.jpg',
        '/styles.css'
      ]);
    })
  );
});

Browser-caching voor terugkerende bezoekers

Nieuwe bezoekers betalen de volledige downloadkosten. Terugkerende bezoekers zouden dat niet hoeven. Met de juiste Cache-Control-headers kun je LCP bij herhaalde bezoeken aanzienlijk verbeteren door het downloaden van afbeeldingen volledig te omzeilen.

Agressieve caching voor versioned assets

<!-- Modern bundlers add hashes to filenames -->
<link rel="stylesheet" href="/styles.a3f2e1.css">
<img src="/hero.4d8b2c.jpg" alt="Hero">

<!-- Server response headers for hashed assets: -->
Cache-Control: public, max-age=31536000, immutable

<!-- Why this works: -->
- Filename changes when content changes
- Browser can cache safely for 1 year
- Repeat visit LCP: ~50ms instead of 800ms
- Cache hit rate: 95%+ for returning visitors

Impact: LCP verbetert van 2,1 s naar 300 ms voor terugkerende bezoekers

Cache revalidatie voor niet-versioned content

<!-- For images without version hashes: -->
Cache-Control: public, max-age=86400, must-revalidate

<!-- Browser behaviour: -->
Day 1: Downloads image (800ms)
Day 2: Checks with server "Is this fresh?" (50ms)
       Server responds: "304 Not Modified"
       Browser uses cached copy (0ms download)

<!-- Result: -->
First visit:  LCP 2.1s (full download)
Repeat visit: LCP 650ms (validation only)

Cache HTML-pagina's niet agressief

Gebruik nooit lange cachetijden voor HTML-pagina's. Die moeten een korte max-age hebben (60–300 seconden) of must-revalidate gebruiken. Anders zien gebruikers verouderde content na een deployment. Cache afbeeldingen en CSS agressief, HTML conservatief.

Speculation Rules API voor instant navigatie

De Speculation Rules API stelt browsers in staat om volledige pagina's te prerenderen voordat de gebruiker klikt. Correct geïmplementeerd kan dit navigatie vrijwel instant maken met een LCP onder de 100 ms.

Basis prefetching met Speculation Rules

<!-- Add to your HTML: -->
<script type="speculationrules">
{
  "prefetch": [
    {
      "urls": ["/products", "/about", "/pricing"],
      "requires": ["anonymous-client-ip-when-cross-origin"],
      "referrer_policy": "no-referrer-when-downgrade"
    }
  ]
}
</script>

<!-- Browser prefetches these pages in the background -->
<!-- When user clicks, page loads instantly from cache -->
<!-- LCP: ~50-100ms instead of 2000ms -->

Agressief prerenderen voor links met hoge zekerheid

<!-- Prerender = full page render in hidden tab -->
<script type="speculationrules">
{
  "prerender": [
    {
      "where": {
        "and": [
          {"href_matches": "/product/*"},
          {"selector_matches": ".product-card a"}
        ]
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

<!-- Browser prerenders likely destination pages -->
<!-- When user clicks, page appears instantly -->
<!-- LCP: Effectively 0ms (already rendered) -->

<!-- Eagerness levels: -->
immediate - Prerender as soon as rule is seen
eager     - Prerender when link is visible
moderate  - Prerender on hover (200ms hover delay)
conservative - Prerender on pointer down

Impact: Vrijwel instant navigatie met een waargenomen LCP onder 100 ms

Browserondersteuning voor Speculation Rules

De Speculation Rules API wordt ondersteund in Chrome 109+ en Edge 109+. In niet-ondersteunde browsers worden de regels stilzwijgend genegeerd zonder negatieve impact. Beschouw het als progressive enhancement: Chrome-gebruikers krijgen instant navigatie, anderen ervaren normale prestaties. Bekijk de huidige browserondersteuning.

Gebruik Speculation Rules verstandig

Prerendering verbruikt bandbreedte en serverresources. Prerender alleen bestemmingen met hoge zekerheid, zoals productpagina's waar gebruikers overheen hoveren. Vermijd het prerenderen van je hele website of pagina's die authenticatie vereisen. Houd prefetch/prerender hit rates bij om te zorgen dat je geen resources verspilt.

Back/Forward Cache (bfcache)

De back/forward cache slaat volledige paginasnapshots op wanneer gebruikers navigeren. Als ze op de terugknop drukken, verschijnt de pagina direct vanuit het geheugen met een LCP van effectief 0 ms.

Zorg dat je pagina's bfcache-compatibel zijn

// Things that break bfcache:

❌ unload event listeners
window.addEventListener('unload', () => {
  // This prevents bfcache
});

✓ pagehide event instead
window.addEventListener('pagehide', () => {
  // This works with bfcache
});

❌ Cache-Control: no-store on page
Cache-Control: no-store

✓ Allow caching (use versioned assets instead)
Cache-Control: public, max-age=300

❌ Open connections (WebSocket, IndexedDB transactions)
const ws = new WebSocket('ws://example.com');
// Must close before page hide

✓ Clean up on pagehide
window.addEventListener('pagehide', () => {
  ws.close();
  transaction.abort();
});

bfcache-compatibiliteit testen

// Chrome DevTools:
// 1. Open DevTools > Application > Back/forward cache
// 2. Navigate to your page
// 3. Click Test back/forward cache button

// Or check programmatically:
window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('Page restored from bfcache');
    // Re-initialize any needed state
  } else {
    console.log('Page loaded normally');
  }
});

// Monitor bfcache usage in analytics
window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    analytics.track('bfcache_restore', {
      page: location.pathname
    });
  }
});

Impact: Terugknopnavigatie toont instant LCP (0–50 ms) voor 60–80% van de gebruikers

bfcache best practice: Test je belangrijkste gebruikerstrajecten met de terugknop. Veel e-commercewebsites breken bfcache door agressieve analytics-tracking of WebSocket-verbindingen, waardoor ze instant terugknopnavigatie mislopen voor productbladertrajecten.

LCP monitoren over tijd

Nadat je LCP hebt geoptimaliseerd, moet je zorgen dat het snel blijft:

Stel Real User Monitoring in

Volg LCP voor elke bezoeker om regressies vroeg te signaleren. Onze observability-implementatie bevat LCP-tracking met volledige context (apparaat, netwerk, locatie).

Bewaak Core Web Vitals in CI/CD

// Run Lighthouse in your CI pipeline
// Fail builds if LCP regresses

const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');

async function runLighthouse(url) {
  const chrome = await chromeLauncher.launch();
  const result = await lighthouse(url, {
    port: chrome.port,
    onlyCategories: ['performance']
  });

  const lcp = result.lhr.audits['largest-contentful-paint'].numericValue;

  if (lcp > 2500) {
    throw new Error(`LCP too slow: ${lcp}ms`);
  }

  await chrome.kill();
}

Stel performance budgets in

Definieer acceptabele normen en stel een alert in zodra ze worden overschreden:

  • LCP: Onder 2,5 s (95e percentiel)
  • Hero-afbeeldingsgrootte: Onder 200 KB
  • TTFB: Onder 800 ms
  • Totale paginagrootte: Onder 1,5 MB

LCP-optimalisatiechecklist

Worstelt je nog steeds met LCP?

Wij kunnen je website auditeren, knelpunten identificeren en bewezen optimalisaties implementeren. Neem contact op of voer een gratis benchmark uit om te zien hoe je er voor staat.

Hulp nodig bij het verbeteren van je LCP?

Wij auditen je website, brengen de oorzaak in kaart en implementeren optimalisaties die daadwerkelijk werken.