Next.js ve Performans Analizi
Next.js Projelerinde Performans Darboğazı Nasıl Bulunur?
Next.js, performans için güçlü araçlar sunar: statik üretim, sunucu taraflı render, otomatik code splitting ve optimize edilmiş Image component. Ancak bu araçların varlığı, projenin otomatik olarak hızlı çalışacağı anlamına gelmez. Yanlış render stratejisi, şişirilmiş client bundle veya kontrol edilmemiş hydration maliyeti, framework'ün sağladığı kazanımları kolayca geride bırakabilir.
Next.js projelerinde performans sorunu bazen açık bir nedene bağlanır: yanlış import edilen büyük bir kütüphane, her sayfada yüklenen bir üçüncü taraf script, gereksiz yere sunucu tarafında işlenen dinamik veri. Bazen ise sorun dağınık ve kümülatif bir yapıdadır; tek bir suçlu yoktur, birkaç küçük karar bir arada gecikme yaratır.
Bu yazıda Next.js'e özgü darboğaz noktalarını ve bunları tespit etmek için kullanılan yöntemleri ele alacağız. Hydration maliyetinden bundle analizine, SSR ve SSG seçiminin performansa etkisinden route bazlı code splitting'e kadar her başlık, ölçüm noktasını ve olası müdahaleyi birlikte gösteriyor.
Hydration maliyeti ve interaktivite gecikmesi
Next.js, sunucu tarafında HTML üretir ve bunu tarayıcıya gönderir. Kullanıcı sayfayı görmeye başlar; ancak etkileşim henüz mümkün değildir. Tarayıcı, JavaScript bundle'ını indirip çalıştırdıktan sonra sunucunun ürettiği HTML'yi React ile eşleştirme işlemi olan hydration'ı tamamlar. Bu aşama tamamlanana kadar butonlar, formlar ve dinamik öğeler yanıt vermez.
INP açısından hydration süresi kritik bir etkendir. Ağır bir JavaScript bundle, hydration'ı geciktirir; bu gecikme süresince kullanıcı etkileşimleri sıraya girer. Chrome DevTools'da bu durum, "Long Tasks" olarak görünür; hydration sırasında ana iş parçacığını uzun süre bloke eden görevler açıkça izlenebilir.
Hydration maliyetini azaltmak için en etkili yaklaşım, client-side JavaScript miktarını düşürmektir. Partial hydration ya da React Server Components kullanımı, sadece etkileşim gerektiren bileşenleri hydrate etmeyi sağlar; salt görüntüleme amacıyla kullanılan bileşenler client-side JS yükü yaratmaz.
Bundle analizi: şişkinliğin kaynağını bulmak
Next.js projelerinde performans sorunlarının önemli bir kısmı, fark edilmeden büyüyen client bundle'larından kaynaklanır. Bir kütüphane eklenirken sadece kullanılan birkaç fonksiyon değil, paketin tamamı bundle'a dahil olabilir. Bu durum, toplam JavaScript boyutunu beklentinin çok üzerine çıkarır.
@next/bundle-analyzer paketi, Next.js build çıktısını görsel bir ağaç haritasına dönüştürür. Her modülün ne kadar alan kapladığını, hangi dependency'lerin en ağır katkıyı yaptığını görmek mümkündür. Özellikle node_modules içinden beklenmedik büyük paketler bu analizde kolayca fark edilir.
JavaScript yükünü ölçerken transfer boyutu ile parse ve compile maliyetini birlikte değerlendirmek gerekir. Sıkıştırılmış bundle boyutu küçük görünse bile, parse süresinde önemli yük oluşturabilir. DevTools'un Performance paneli bu ayrımı görünür kılar.
SSR ve SSG seçiminin performansa doğrudan etkisi
getServerSideProps her istekte sunucu tarafında çalışır ve TTFB üzerinde doğrudan iz bırakır. Veritabanı sorgusu veya harici API çağrısı içeriyorsa bu süre, dış sistemlerin yanıt süresine bağımlı hale gelir. Yavaş bir upstream kaynakla birleşince TTFB kolayca yükselebilir.
getStaticProps ise build sırasında çalışır ve statik HTML üretir. İstek başına sunucu işlemi yoktur; içerik CDN'den doğrudan sunulur. Bu yaklaşım TTFB açısından en iyi sonucu verir. Ancak sık güncellenen içerik için Incremental Static Regeneration (ISR) gerekebilir; bu da cache geçerliliği yönetimini gündeme getirir.
Next.js 13 ve sonrasında gelen App Router ile Server Components varsayılan davranış haline geldi. Bu modelde client-side JavaScript'i minimize etmek mimari kararların merkezine oturur. Hangi bileşenin server, hangisinin client tarafında render edileceğini bilinçli seçmek, hem bundle boyutunu hem de hydration maliyetini kontrol altında tutar.
Route bazlı code splitting ve dinamik import
Next.js, her sayfa için otomatik olarak ayrı JavaScript chunk'ları üretir. Anasayfa kodu, ürün sayfasıyla birlikte gönderilmez. Bu davranış varsayılan olarak gelir ve tek başına önemli bir kazanım sağlar. Ancak büyük projelerde bu otomatik ayrım yeterli olmayabilir; ortak (shared) chunk boyutları beklenmedik şekilde şişebilir.
Dinamik import, bileşenlerin ihtiyaç anında yüklenmesini sağlar. next/dynamic ile modal, ağır form bileşeni veya sadece belirli koşullarda görünen bir panel, ilk sayfa yüklemesinden ertelenebilir. Bu erteleme, başlangıç bundle boyutunu küçültür ve LCP süresini doğrudan etkiler.
Hangi bileşenlerin dinamik import adayı olduğunu belirlemek için Coverage paneli kullanışlıdır. İlk yükleme sırasında hiç çalıştırılmayan kod, erteleme için açık bir işarettir. Bu tespiti periyodik olarak yapmak, büyüyen projelerde kontrolsüz bundle artışını önler.
next/image component'inin doğru kullanımı
Next.js'in next/image bileşeni otomatik olarak boyutlandırma, WebP dönüşümü ve lazy loading sağlar. Ancak bu avantajlar yanlış kullanımda tersine döner. Hero görsel için priority prop'u verilmezse görsel lazy olarak yüklenir; bu durumda LCP öğesi geç yüklenir ve skor gereksiz yere düşer.
width ve height prop'ları belirtilmezse ya da yanlış değerler girilirse görsel alanı sayfa yüklenirken kayar; bu da CLS sorununa yol açar. Görsel boyutları her zaman gerçek kaynak boyutlarıyla eşleşmeli veya fill prop'u uygun container kısıtlamasıyla kullanılmalıdır.
Dış kaynaklı görseller için next.config.js'de domain izni verilmesi gerekir. Bu konfigürasyon unutulursa görseller yüklenmez; bazen geçici çözüm olarak unoptimized prop'u eklenir. Bu yaklaşım, format dönüşümü ve boyutlandırma avantajlarını ortadan kaldırır.
Font optimizasyonu: next/font ile üçüncü taraf bağımlılığını kesmek
Next.js 13 ile birlikte gelen next/font modülü, Google Fonts'u build aşamasında indirir ve font dosyalarını uygulamanın kendi altyapısından sunar. Kullanıcı tarayıcısı Google Fonts domain'ine bağlanmak zorunda kalmaz; DNS çözümlemesi ve bağlantı maliyeti ortadan kalkar.
Aynı modül, font-display: optional stratejisini varsayılan olarak uygular ve layout shift'i önlemek için CSS değişkenleriyle fallback font metriklerini eşleştirir. Bu otomatik yapı, manuel font optimizasyonunda çoğunlukla gözden kaçan birkaç adımı bir arada çözer. Ancak next/font kullanılmadan doğrudan Google Fonts link etiketi eklenmesi, bu kazanımların tamamını geçersiz kılar.
Third-party script yönetimi: next/script stratejileri
next/script bileşeni, üçüncü taraf scriptlerin yükleme önceliğini kontrol etmek için üç strateji sunar: beforeInteractive, afterInteractive ve lazyOnload. Çoğu analitik ve reklam scripti afterInteractive ile yüklenebilir; sayfa etkileşime açıldıktan sonra çalışması yeterlidir.
Tag manager gibi araçları doğrudan HTML'e script etiketi olarak eklemek yerine next/script üzerinden kontrol etmek, yükleme sırası üzerinde belirli bir hakimiyet sağlar. Ancak bu yapı, tag manager içinden enjekte edilen script'leri kapsamaz; container içindeki yük yine ayrıca değerlendirilmelidir.
Development ve production ortamı arasındaki performans farkı
Next.js development modunda (next dev) kod sıkıştırılmaz, bundle'lar birleştirilmez ve bazı optimizasyonlar devre dışıdır. Bu modda yapılan performans ölçümleri, production davranışını yansıtmaz. Gerçek darboğazı görmek için next build ve next start ile production build üzerinde ölçüm almak gerekir.
Vercel ortamında deploy edilen projelerde Vercel Speed Insights, gerçek kullanıcı verisi toplar ve Core Web Vitals değerlerini route bazında raporlar. Bu veri, hangi sayfanın gerçek kullanıcılarda sorun yarattığını gösterir; lab ölçümünün göremediği kullanıcı segmenti farklılıklarını da yansıtır.
Server Components ve client sınırını doğru çizmek
App Router ile Server Components varsayılan olarak gelir. Bir bileşen use client direktifi içermiyorsa sunucu tarafında render edilir ve client-side JavaScript bundle'ına dahil olmaz. Bu mimari, JavaScript yükünü minimize etmek için güçlü bir araçtır.
Ancak use client direktifi ağaçta bir kez eklendiğinde, o bileşenin tüm alt bileşenleri de client bundle'ına dahil olur. Bu durum, yanlış yerleştirilmiş bir direktifin düşündüğünden çok daha fazla kodu client'a taşımasına yol açar. Bileşen sınırlarını dikkatli çizmek, bu sızıntıyı önlemenin temel yoludur.
JavaScript yükleme stratejisi genel olarak önemlidir; Next.js bağlamında ise bu strateji bileşen mimarisine kadar iner. Hangi verinin server'da kalabileceğini, hangi etkileşimin client gerektirdiğini bileşen tasarımı aşamasında belirlemek, ilerleyen aşamada yapılacak optimize çabalarını büyük ölçüde azaltır.
Vercel Analytics ve Speed Insights ile sürekli izleme
Vercel Analytics, her deploy sonrasında performans verisi toplamaya başlar. Core Web Vitals değerleri, route bazında ayrıştırılmış olarak gösterilir; belirli bir sayfanın belirli bir metrikte sorun yaşadığı açıkça görülebilir. Bu veri, lab ölçümünün yakalayamadığı gerçek kullanıcı koşullarını yansıtır.
Speed Insights ise sayfa bazında daha ayrıntılı veri sunar; hangi kullanıcı segmentinin hangi metrikte zayıf sonuç gördüğünü görmek mümkündür. Mobil ve masaüstü verisi ayrı izlenebilir; belirli bir ülkedeki kullanıcıların farklı bir deneyim yaşayıp yaşamadığı bu araçla tespit edilebilir.
CI entegrasyonu ile performans sınırı koymak
Next.js projelerinde performans regresyonunu en erken aşamada yakalamak için CI/CD hattına otomatik ölçüm eklemek etkili bir yaklaşımdır. Her pull request sonrasında Lighthouse veya bundle boyutu kontrolü çalıştırılabilir; belirlenen eşik değerlerin aşılması build'i başarısız sayar.
Performans bütçesi bu sürecin temelini oluşturur. JavaScript bundle boyutu için maksimum limit, kritik sayfalar için LCP veya TTI hedefi bütçe olarak tanımlanabilir. Bu bütçeler olmadan büyüyen projelerde her sprint sonrasında küçük bir regresyon birikir; zamanla ölçüm yapıldığında performans başlangıç noktasının çok gerisinde çıkabilir.
Next.js, doğru kullanıldığında performans açısından güçlü bir temel sunar. Ancak framework'ün sunduğu araçlar bilinçli seçimler gerektiriyor. Render stratejisi, bileşen sınırları ve bundle yönetimi, birbirinden bağımsız teknik kararlar gibi görünse de bir arada değerlendirildiğinde gerçek kullanıcı deneyimini belirliyor.
Darboğazı bulmak için önce production ortamında ölçüm almak, ardından bundle analizi ile büyük katkı yapan paketleri tespit etmek, son olarak hydration ve render stratejisini incelemek mantıklı bir sıra oluşturur. Her adım, bir sonraki müdahalenin önceliğini netleştirir.