03.02.2026

React’te “Skeleton” Değil, “Shimmer” Değil: İçerik-Öncelikli (Content-Aware) Loading UI Tasarımı

Tek tip skeleton yerine, veri şekline göre yükleme arayüzleriyle algılanan performansı artırın.

Yükleme ekranı denince çoğu projede refleks aynı: her yerde gri kutular (skeleton). Ancak bu yaklaşım her zaman en iyi algıyı vermez. Kullanıcı aslında hangi içeriğin geleceğini anlamak ister. Bu yazıda “content-aware loading UI” yaklaşımıyla, yükleme durumunu verinin biçimine göre tasarlayıp daha anlaşılır ve daha az “zıplayan” (layout shift) arayüzler üretmeyi konuşacağız.

Problem: Tek tip skeleton neden yetersiz?

  • Belirsizlik: Skeleton, gelecek içeriğin türünü anlatmıyorsa kullanıcı “ne beklediğini” bilmez.
  • Yanıltıcı hız: Shimmer animasyonu bazen “hızlıymış” hissi verir ama gecikme uzadığında dikkat dağıtır.
  • Layout shift riski: İçerik gelince boyutlar değişiyorsa sayfa gözle görülür şekilde oynar.

Hedef: Yükleme UI’ı, gelecek içeriğin yapısını dürüstçe temsil etsin ve yerleşim baştan otursun.

Yaklaşım: “İçerik Şekli” (content shape) ile yükleme

Bir liste sayfasını düşünelim: ürün kartı, fiyat, etiket, buton. Skeleton yerine, kartın semantiğini anlatan “placeholder”lar üretelim:

  • Görsel alanı için sabit oran kutu
  • Başlık için 2 satır
  • Fiyat için kısa bir satır
  • Etiket için küçük bir kapsül

1) Yerleşimi sabitle: aspect-ratio + min-height

function ProductCardShell() {
  return (
    <div className="card" aria-hidden="true">
      <div className="media" />
      <div className="lines">
        <div className="line w-80" />
        <div className="line w-60" />
        <div className="row">
          <div className="pill w-24" />
          <div className="line w-20" />
        </div>
      </div>
    </div>
  );
}

.card { display: grid; gap: 12px; }
.media { aspect-ratio: 4 / 3; background: #eee; border-radius: 12px; }
.lines { display: grid; gap: 8px; }
.line { height: 12px; background: #eee; border-radius: 6px; }
.pill { height: 20px; background: #eee; border-radius: 999px; }
.w-80 { width: 80%; }
.w-60 { width: 60%; }
.w-24 { width: 96px; }
.w-20 { width: 72px; }
.row { display: flex; align-items: center; justify-content: space-between; }

Bu tasarımın farkı: “gri kutu” değil, ürün kartı geleceği belli.

2) Sayı kadar shell üret: boşluk hissini azalt

function ProductGrid({ products, isLoading }) {
  if (isLoading) {
    return (
      <div className="grid">
        {Array.from({ length: 8 }).map((_, i) => (
          <ProductCardShell key={i} />
        ))}
      </div>
    );
  }

  return (
    <div className="grid">
      {products.map(p => (
        <ProductCard key={p.id} product={p} />
      ))}
    </div>
  );
}

İpucu: Shell sayısını ekran genişliğine göre değil, kullanıcının ilk bakışta göreceği “above the fold” sayıya göre belirlemek genelde daha iyi hissettirir.

İçerik-öncelikli durumlar: “yok” ile “yükleniyor”yu ayır

Sık yapılan hata: veri yokken de skeleton göstermek. Oysa “0 sonuç” ayrı bir durumdur.

function Results({ data, isLoading, query }) {
  if (isLoading) return <ResultsShell />;

  if (!data.length) {
    return (
      <div role="status">
        <h3>Sonuç bulunamadı</h3>
        <p>“{query}” için farklı bir arama deneyin.</p>
      </div>
    );
  }

  return <ResultsList items={data} />;
}

Bu ayrım, kullanıcıya “sistem çalışıyor mu, yoksa gerçekten sonuç yok mu?” sorusunun cevabını net verir.

Erişilebilirlik: placeholder’ları sessiz tut

  • Shell bileşenleri okuyucu teknolojilere içerik gibi görünmemeli.
  • Yükleme başladığında küçük bir metinle durum bildirimi faydalı olabilir.

Öneri:

  • Shell: aria-hidden="true"
  • Sayfa genelinde: role="status" içeren küçük bir “Yükleniyor…” metni

Animasyon kullanacaksan: “reduce motion”a saygı

Shimmer yerine hafif bir “pulse” tercih edin ve hareket azaltma ayarını kontrol edin:

@media (prefers-reduced-motion: reduce) {
  .pulse { animation: none; }
}

Sonuç

İyi bir loading UI, sadece “bekleme”yi maskelemek değil; gelecek içeriği doğru anlatmak ve yerleşimi baştan stabil kurmaktır. Skeleton’ı körlemesine kopyalamak yerine, veri şekline göre “content-aware” placeholder’lar tasarlayın: kullanıcı daha az şaşırır, arayüz daha profesyonel görünür.