PERFORMANS12m READ14 Haziran 2026

Core Web Vitals: LCP, FID ve CLS Optimizasyonu

LCP, INP ve CLS metriklerini iyileştirme teknikleri.

Core Web Vitals, Google'ın kullanıcı deneyimini ölçmek için belirlediği üç temel metriktir. Bu metrikler hem SEO sıralamasını hem de kullanıcı memnuniyetini doğrudan etkiler. 2024 itibarıyla FID yerini INP'ye bırakmış ve ölçüm kriterleri güncellemiştir.

Üç Temel Metrik

// PLAINTEXT //
LCP — Largest Contentful Paint
"Sayfa içindeki en büyük görsel/metin ne zaman yüklendi?"
İdeal: < 2.5 sn | Geliştirilmeli: 2.5-4 sn | Kötü: > 4 sn
 
INP — Interaction to Next Paint (FID yerine)
"Kullanıcı etkileşimine görsel yanıt ne kadar sürdü?"
İdeal: < 200 ms | Geliştirilmeli: 200-500 ms | Kötü: > 500 ms
 
CLS — Cumulative Layout Shift
"Sayfa yüklenirken layoutta ne kadar kayma oldu?"
İdeal: < 0.1 | Geliştirilmeli: 0.1-0.25 | Kötü: > 0.25

LCP Optimizasyonu

// HTML //
<!-- LCP elementi genellikle hero görseli veya büyük başlık -->
<!-- 1. Preload ile öncelikli yükleme -->
<link rel="preload" as="image" href="/hero.webp"
  imagesrcset="/hero-480.webp 480w, /hero-960.webp 960w"
  imagesizes="100vw"
  fetchpriority="high">
 
<!-- 2. fetchpriority="high" — tarayıcıya öncelik ver -->
<img src="/hero.webp"
  fetchpriority="high"
  loading="eager"   <!-- lazy değil! -->
  decoding="sync"   <!-- async değil! -->
  alt="Ana görsel"
  width="1200" height="630">
// JAVASCRIPT //
// Next.js — LCP görseli optimize et
import Image from 'next/image';
 
export default function Hero() {
  return (
    <Image
      src="/hero.webp"
      alt="Hero"
      width={1200}
      height={630}
      priority          // fetchpriority="high" + preload
      placeholder="blur"
      blurDataURL="data:image/webp;base64,..."
    />
  );
}
// NGINX //
# 3. Sunucu yanıt süresini düşür (TTFB < 600ms)
# Nginx — HTTP/2 server push
location / {
    http2_push /styles.css;
    http2_push /hero.webp;
    add_header Cache-Control "public, max-age=31536000, immutable";
}
 
# 4. CDN + Edge Caching
# Cloudflare, Vercel Edge, CloudFront ile statik içeriği edge'e al

INP (Interaction to Next Paint) Optimizasyonu

// JAVASCRIPT //
// INP: kullanıcı tıklayınca bir sonraki "paint"e kadar geçen süre
// Sorun: Main thread bloğu
 
// ❌ Kötü — uzun görev main thread'i bloklar
button.addEventListener('click', () => {
  const sonuc = agirHesaplama(1000000); // 500ms blok
  goster(sonuc);
});
 
// ✅ İyi — scheduler.yield() ile main thread'i serbest bırak
button.addEventListener('click', async () => {
  gosterYukleniyor(true);
 
  // Main thread'i serbest bırak — tarayıcı animate, layout yapabilir
  await scheduler.yield(); // Chrome 115+
 
  const sonuc = await agirHesaplamaAsync();
  gosterYukleniyor(false);
  goster(sonuc);
});
 
// ✅ Daha geniş destek — setTimeout ile yield
function verim() {
  return new Promise(r => setTimeout(r, 0));
}
 
async function uzunGo() {
  for (const parca of parcalar) {
    isle(parca);
    await verim(); // Her parçadan sonra yield
  }
}
// JAVASCRIPT //
// React — Concurrent Features ile INP iyileştirme
import { startTransition, useTransition } from 'react';
 
function AramaKutusu() {
  const [aramaMetni, setAramaMetni] = useState('');
  const [sonuclar, setSonuclar]     = useState([]);
  const [isPending, startTransition] = useTransition();
 
  function handleChange(e) {
    // Anlık güncellemeler — yüksek öncelik
    setAramaMetni(e.target.value);
 
    // Ağır UI güncellemesi — düşük öncelik, main thread'i bloklamaz
    startTransition(() => {
      setSonuclar(aramaYap(e.target.value));
    });
  }
 
  return (
    <>
      <input value={aramaMetni} onChange={handleChange} />
      {isPending && <p>Aranıyor...</p>}
      <SonucListesi sonuclar={sonuclar} />
    </>
  );
}

CLS Optimizasyonu

// CSS //
/* CLS: görsel kayması — görsel/font/dinamik içerik nedeniyle */
 
/* 1. Görsel boyutlarını daima belirt */
img, video {
  width: 100%;
  height: auto;
  aspect-ratio: 16/9; /* Ya da width/height atributu */
}
 
/* 2. Font kaymasını önle */
@font-face {
  font-family: 'Yazitipim';
  src: url('/font.woff2') format('woff2');
  font-display: optional; /* swap yerine optional — daha az CLS */
}
 
/* 3. Dinamik içerik için rezervasyon */
.reklam-alani {
  min-height: 250px; /* İçerik yüklenmeden önce yer kapla */
}
// HTML //
<!-- 4. Geç yüklenen içerik için placeholder -->
<div style="min-height: 400px">
  <!-- İçerik buraya gelecek — layout kayması yok -->
  <noscript>
    <p>JavaScript gerekli</p>
  </noscript>
</div>
 
<!-- 5. Skeleton loading — CLS'ye katkısı yok -->
<div class="skeleton" aria-hidden="true">
  <div class="skeleton-line" style="width: 60%"></div>
  <div class="skeleton-line" style="width: 80%"></div>
</div>

Ölçüm ve İzleme

// JAVASCRIPT //
// Web Vitals kütüphanesi ile ölçüm
import { onLCP, onINP, onCLS } from 'web-vitals';
 
function gonder(metrik) {
  fetch('/api/vitals', {
    method: 'POST',
    body: JSON.stringify(metrik),
    keepalive: true, // Sayfa kapanırken de gönder
  });
}
 
onLCP(gonder);
onINP(gonder);
onCLS(gonder);
 
// Next.js — yerleşik Web Vitals raporlama
export function reportWebVitals(metrik) {
  switch (metrik.name) {
    case 'LCP': analytics.track('LCP', { deger: metrik.value }); break;
    case 'INP': analytics.track('INP', { deger: metrik.value }); break;
    case 'CLS': analytics.track('CLS', { deger: metrik.value }); break;
  }
}
// BASH //
# Lighthouse CI ile otomatik ölçüm
npm install -g @lhci/cli
 
# lighthouserc.js
module.exports = {
  ci: {
    collect: { url: ['http://localhost:3000'] },
    assert: {
      assertions: {
        'largest-contentful-paint': ['warn', { maxNumericValue: 2500 }],
        'cumulative-layout-shift':  ['error', { maxNumericValue: 0.1 }],
        'interactive':              ['warn', { maxNumericValue: 3500 }],
      },
    },
  },
};

Hızlı Kontrol Listesi

// PLAINTEXT //
LCP < 2.5 sn:
☐ Hero görseli WebP/AVIF formatında
☐ fetchpriority="high" eklendi
☐ <link rel="preload"> eklendi
☐ CDN üzerinden sunuluyor
☐ TTFB < 600ms (sunucu hızlı yanıt veriyor)
 
INP < 200 ms:
☐ Uzun görevler (>50ms) parçalara bölündü
☐ scheduler.yield() veya setTimeout(0) kullanıldı
☐ React: startTransition ile ağır güncellemeler
☐ Third-party script'ler defer/async
 
CLS < 0.1:
☐ Tüm img/video elementlerinde width+height var
☐ font-display: optional veya swap
☐ Dinamik içerik için min-height rezervasyonu
☐ Reklam alanları için yer tutucu

Özet

LCP için görsel optimizasyonu ve preload, INP için main thread boşaltma ve Concurrent React, CLS için boyut rezervasyonu ve font stratejisi. Her metriği ayrı ayrı ele alın; gerçek kullanıcı verisini Lighthouse CI ile takip edin ve performans regresyonlarını deploy öncesi yakalayın.