decoding="async" Nedir? Görsellerde Ne İşe Yarar?
decoding="async" HTML'de sık karşılaşılan ama ne yaptığı çoğunlukla bilinmeyen bir attribute. Framework çıktılarında otomatik ekleniyor, örneklerden kopyalanıyor, performans rehberlerinde öneriliyor — ama hangi sorunu çözdüğü, hangi durumlarda doğru, hangi durumlarda yanlış olduğu nadiren açıklanıyor. Bu belirsizlik pratikte bir yanlış kullanım kalıbı üretiyor: attribute tüm görsellere körce ekleniyor ve asıl fark yaratabileceği görsellerde yanlış değer kullanılıyor.
decoding attribute'u tarayıcıya görsel dosyasının baytlarını piksel verisine dönüştürme — yani decode etme — işleminin nasıl yapılacağını söyler. async değeri bu işlemi ana iş parçacığından (main thread) ayırır ve ayrı bir işçi thread üzerinde çalıştırır. Ana thread, serbest kaldığı için diğer işlere — JavaScript çalıştırma, düzen hesaplama, kullanıcı etkileşimlerine yanıt verme — devam edebilir. Görsel biraz daha sonra ekrana gelir; ama sayfa bu sürede donmaz.
Bu trade-off çoğu görsel için iyi bir seçim. Ama sayfanın LCP adayı olan büyük hero görselinde aynı trade-off tam tersi sonuç verir. Decode işlemini geciktirmek, görselin ekrana gelmesini geciktirir; bu da LCP süresini doğrudan uzatır. decoding attribute'unu doğru kullanmak, bir değer seçmekten çok hangi görselde hangi değerin seçileceğini bilmekten geçiyor.
Tarayıcı görsel decode'u nasıl yapar
Bir görsel dosyası ağdan indirildiğinde tarayıcıya sıkıştırılmış bayt dizisi olarak ulaşır. JPEG, PNG veya WebP formatlarının her biri farklı bir sıkıştırma algoritması kullanır; bu baytları ekranda gösterilecek piksel verisine dönüştürmek için ayrıştırma ve hesaplama işlemi gerekir. İşte bu aşamaya decode deniyor. İndirme ve decode iki ayrı adım; dosya ağdan tamamen geldikten sonra decode başlar.
Decode işlemi CPU'ya bağlı ve büyüklüğüyle orantılı bir işlem. 100×100 piksellik küçük bir thumbnail milisaniyeler içinde decode edilir. 2000×1500 piksellik yüksek çözünürlüklü bir fotoğraf ise özellikle düşük güçlü cihazlarda onlarca, hatta yüzlerce milisaniye sürebilir. Bu süre boyunca tarayıcının hangi thread'inin meşgul olduğu kritik önem taşır.
Tarayıcının ana thread'i tek bir iş akışını yönetir: HTML ayrıştırma, CSS hesaplama, JavaScript çalıştırma, düzen ve boyama işlemleri ve kullanıcı etkileşimleri hepsi bu tek thread üzerinde sırayla çalışır. Ana thread meşgulken kullanıcının tıklamaları yanıtsız kalır, kaydırma sekser, animasyonlar takılır. Büyük bir görseli bu thread üzerinde decode etmek, decode tamamlanana kadar sayfanın diğer her şeyi beklemesi anlamına gelir.
decoding="async" ana thread'i neden serbest bırakır
decoding="async" tarayıcıya şunu söyler: bu görselin decode işlemini ana thread dışında, uygun bir zamanda ve ayrı bir işçi thread üzerinde yap. Tarayıcı bu talebi alır, decode işini kuyruğa ekler ve ana thread'i serbest bırakır. Görsel, decode işçi thread'de tamamlandığında ekrana gelir. Bu süre zarfında ana thread başka işlere devam etmiştir.
Kullanıcı deneyimi açısından sonuç şudur: görsel ekrana biraz daha geç gelir, ama sayfa bu gecikme sırasında yanıt vermeye devam eder. Kaydırma akıcı kalır, JavaScript çalışır, etkileşimler yanıt alır. Özellikle çok sayıda görsel barındıran sayfalarda — galeri, ürün listesi, uzun blog yazıları — birden fazla görselin aynı anda decode edilmesi gereken durumlar sıktır. Bu görsellerin hepsini ana thread üzerinde decode etmek, her birinin sırayla ana thread'i bloke etmesi demektir. decoding="async" bu yükü işçi thread'lere dağıtır.
decoding="auto" (ya da attribute hiç verilmemişse varsayılan değer olan auto) ise kararı tarayıcıya bırakır. Modern tarayıcılar bu kararı duruma göre verir; küçük görseller için sync, büyük görseller için async tercih edebilir. decoding="sync" ise decode işlemini açıkça ana thread üzerinde ve hemen yapar — görsel mümkün olan en hızlı şekilde ekrana gelir, ama bu sürede ana thread bloke kalır.
Next.js'in Image bileşeni varsayılan olarak tüm görsellere decoding="async" ekler. Bu makul bir varsayılan; sayfadaki görsellerin büyük çoğunluğu için doğru seçim. Ama bileşenin priority prop'u verildiğinde bu davranış değişir ve LCP görseli için daha uygun bir decode stratejisi devreye girer.
LCP görseline sync mi async mi
Largest Contentful Paint, sayfanın viewport'unda görünen en büyük içerik öğesinin ekrana gelme süresini ölçer. Bu metrik genellikle sayfanın hero görseli ya da üst kısımdaki büyük bir fotoğraf tarafından belirlenir. LCP ölçümü, görselin tam olarak boyandığı — ekranda piksel olarak görünür hale geldiği — anı kaydeder. Boyama ise decode tamamlanmadan gerçekleşemez.
decoding="async" ile LCP adayı bir görsel şu sırayla işlenir: baytlar ağdan iner, decode işi kuyruğa eklenir, işçi thread'in sırası geldiğinde decode tamamlanır, ardından boyama gerçekleşir. Kuyruktaki bekleme süresi ve işçi thread'in müsaitliğine bağlı gecikme, LCP ölçümüne eklenir. Bu gecikme koşullara göre değişir; bazen fark edilmez düzeyde küçük, bazen onlarca milisaniye olabilir.
decoding="sync" ile aynı görsel şöyle işlenir: baytlar iner, ana thread hemen decode yapar, ardından boyama gerçekleşir. Kuyruk bekleme süresi yoktur. Görsel mümkün olan en kısa sürede ekrana gelir. Evet, bu sürede ana thread meşguldür; ama LCP adayı bir görsel söz konusu olduğunda bu bekleme genellikle tercih edilir — çünkü LCP skoru, görselin ekrana gelmesini bir an önce kaydetmekten geçer.
Pratik kural bu nedenle nettir: LCP adayı görsele ya decoding="sync" verin, ya da attribute'u hiç koymayın (auto tarayıcı kararına bırakır ve modern tarayıcılar LCP adaylarını genellikle doğru tespit eder). fetchpriority="high" ile bu görsel zaten indirme kuyruğunda öne alınmıştır; decode aşamasında da async kuyruğuna girmemesi tutarlı bir yaklaşım. Diğer tüm görseller — below-the-fold, galeri, ürün listesi, dekoratif görseller — için decoding="async" doğru seçimdir.
decoding ile fetchpriority ve loading arasındaki ilişki
Bu üç attribute görsel yükleme pipeline'ının farklı aşamalarını etkiler ve birbirinin yerine kullanılamaz. fetchpriority ve preload ağ katmanında çalışır: görsel baytlarının ne zaman ve hangi öncelikle talep edileceğini belirler. loading görselinin görünür alana yaklaşana kadar hiç indirilmeyip indirilmeyeceğini belirler. decoding ise baytlar ağdan geldikten sonra devreye girer ve decode işleminin hangi thread'de, ne zaman yapılacağını yönetir.
Bu üçünü doğru kombinasyonlarla kullanmak gerekiyor. İlk ekranda görünen LCP adayı bir hero görseli için ideal kombinasyon şöyle kurulabilir: fetchpriority="high" indirme önceliğini artırır, loading="eager" (ya da attribute yok) görselin hemen indirilmesini sağlar, decoding="sync" decode işini ana thread'de ve hemen yapar. Tüm bu ayarlar birlikte görselin olabildiğince hızlı ekrana gelmesini hedefler.
Below-the-fold bir galeri görseli için ise kombinasyon tersine döner: loading="lazy" görsel görünür alana yaklaşana kadar indirmeyi erteler, decoding="async" decode işini ana thread dışında yürütür. Lazy loading'in istisnaları göz önüne alındığında, bu iki attribute birlikte kullanıldığında below-the-fold görseller için hem bant genişliği hem de main thread açısından tasarruf sağlanır.
Tutarsız kombinasyonlar dikkat gerektiriyor. fetchpriority="high" ile decoding="async"'i aynı LCP görseline vermek, "hızlıca indir" ve "decode işini ertele" sinyallerini çakıştırır. İndirme erkenden tamamlanır ama decode kuyruğunda bekleme, bu kazanımı kısmen geri alabilir. Görsellerinizi sınıflandırırken her attribute grubunu birlikte düşünmek, tutarsız kombinasyonları önler.
Pratikte ne zaman fark yaratır, ne zaman yaratmaz
Yüksek performanslı masaüstü donanımında, çok çekirdekli işlemcilerde, decoding="async" ile decoding="sync" arasındaki fark çoğunlukla ölçülemez düzeyde küçük. Ana thread'in diğer görevler için yeterince kaynağı var; büyük bir görseli sync decode etsek bile sayfa yanıt vermeye devam eder. Bu cihazlarda attribute'un değeri performans üzerinde kayda değer bir iz bırakmaz.
Düşük güçlü mobil cihazlarda tablo farklılaşır. Tek ya da çift çekirdekli işlemcilerde ana thread ciddi biçimde sınırlı bir kaynak. Büyük bir görseli bu thread üzerinde sync decode etmek, decode tamamlanana kadar kaydırmanın seksmesine, dokunuşların gecikmesine, animasyonların takılmasına yol açabilir. Bu cihazlarda decoding="async" kullanıcı deneyimine somut katkı sağlar — özellikle birden fazla görsel aynı anda ekrana girdiğinde.
Görsel boyutu da kritik bir değişken. Küçük görseller — thumbnail, avatar, ikon — için decode işlemi o kadar hızlı tamamlanır ki sync ve async arasındaki fark hiçbir koşulda fark edilmez. Asıl anlam büyük dosyalarda ortaya çıkar: yüksek çözünürlüklü ürün fotoğrafları, full-width hero görselleri, panoramik çekimler. Bu dosyalar için decoding="async" — LCP adayı değillerse — main thread'i korumanın en kolay yollarından biri.
Framework'lerin varsayılan davranışı bu pratiği yansıtıyor. Çoğu modern framework ve statik site üreticisi tüm görsellere decoding="async" ekler. Bu muhafazakâr ama makul bir seçim — görsellerin büyük çoğunluğu için doğru, LCP adayı görsel için override gerektiriyor. Override'ı unutmak sık karşılaşılan hatalardan biri. Sayfayı Lighthouse ile analiz ettiğinizde "Largest Contentful Paint element" bölümünün işaret ettiği görselin decoding değerini kontrol etmek, bu hatayı yakalamanın en hızlı yolu.
decoding="async"'in işlevi anlaşıldığında kullanım kararları basitleşiyor. Sayfadaki görselleri iki gruba ayırmak yeterli: ilk ekranda görünen ve LCP adayı olan görsel ile diğerleri. İlki için decoding="sync" ya da attribute yok; diğerleri için decoding="async". Bu ayrım bir kez yapıldıktan sonra template düzeyinde tutarlı biçimde uygulanabilir.
Attribute'u hiç düşünmeden her görsele eklemek ya da hiçbir görsele eklemeksizin bırakmak da aslında büyük bir sorun yaratmaz — tarayıcıların auto davranışı çoğu durumda makul kararlar verir. Ama LCP adayını açıkça decoding="sync" ile işaretlemek ve geri kalanı decoding="async" olarak belirlemek, tarayıcıya net bir sinyal verir ve bu sinyalin LCP skoruna olumlu yansıma ihtimali oldukça yüksektir.