Hydration completed but contains mismatches.💥
SSR (Server-Side Rendering) modern framework’lerin sunduğu en büyük avantajlardan biridir -- hızlı yükleme, SEO uyumluluğu ve ilk render performansı. Ancak bu gücün bir yan etkisi vardır: Hydration Mismatch.
Eğer Vue veya Nuxt projesinde terminalde şöyle bir uyarı gördüysen, yalnız değilsin:
Hydration completed but contains mismatches.
Bu hata gizemli görünür ama aslında çok mantıklıdır. Bu yazıda “hydration” sürecinin tam olarak ne olduğunu, neden “mismatch” oluştuğunu ve bunu nasıl önleyebileceğini anlatacağız.
Hydration Nedir?
Hydration, sunucuda oluşturulan HTML’in tarayıcıda yeniden Vue instance’ı ile eşleştirilmesi (rehydrate edilmesi) sürecidir.SSR sayfası oluşturduğunda, kullanıcıya şu akış gerçekleşir:
- Server Render: Nuxt, sayfayı HTML olarak üretir ve tarayıcıya gönderir.
- Client Hydration: Tarayıcı bu HTML’i alır, Vue uygulamasını yeniden başlatır ve aynı sanal DOM yapısını oluşturur.
- Vue, HTML ve sanal DOM’u karşılaştırır → her şey aynıysa “hydration başarılı” der.
Özetle: SSR HTML ≠ Client render sonucu → Hydration Mismatch.
En Yaygın Sebepler
1. Zamana veya Tarayıcıya Bağımlı Kod
<p>{{ new Date().toLocaleTimeString() }}</p>
Server zamanı 12:00, client zamanı 12:01 olabilir. Tarayıcı bu farkı görünce DOM uyuşmazlığı oluşur. 😬✅ Çözüm: Tarayıcıya özel kodu onMounted() içine al.
<script setup>
import { ref, onMounted } from 'vue'
const time = ref('')
onMounted(() => time.value = new Date().toLocaleTimeString())
</script>
2. Random (Rastgele) Değerler
<p>{{ Math.random() }}</p>
Server ve client tarafında farklı değerler üretildiği için DOM asla eşleşmez.✅ Çözüm: Random değeri ya serverda üretip prop olarak geçir, ya da client-side oluştur.
3. Window veya Document Erişimi
Server tarafındawindow veya document yoktur.const width = window.innerWidth // ❌ SSR patlar
Bu tür kodlar client tarafında çalıştırılmalıdır.✅ Çözüm:
if (process.client) {
const width = window.innerWidth
}
veya Composition API’de:
onMounted(() => {
console.log(window.innerWidth)
})
4. Koşullu Render Farkları
<div v-if="isClient">Tarayıcı Render</div>
Eğer isClient değeri server ve client arasında farklıysa, Vue farklı DOM oluşturur.✅ Çözüm: SSR ile client arasında aynı başlangıç state’ini koru. Gerekirse useState() kullan.
5. Async Veri Uyuşmazlığı
useAsyncData ile çekilen veriler SSR sırasında hazır olmalı. Eğer client tarafında yeniden fetch ediliyorsa DOM farkı oluşabilir.✅ Çözüm: Her useAsyncData çağrısına unique key ver.
await useAsyncData('posts', () => $fetch('/api/posts'))
Aksi halde Nuxt, cache yerine yeniden fetch eder ve içerik farkı yaratır.
Hydration Akışını Anlamak
Vue şu sırayla çalışır:- SSR sırasında HTML oluşturulur.
- Tarayıcıda Vue app yeniden başlatılır.
- Vue, SSR HTML ile kendi render’ını karşılaştırır.
- Fark varsa, konsola
hydration mismatchuyarısı yazar. - Ardından Vue DOM’u yeniden oluşturur (görsel fark olmasa da performans kaybı oluşur).
Hataların Tespitinde Yardımcı Araçlar
console.warn’da detaylı mesajlar: Vue genellikle hangi node’un uyuşmadığını belirtir.nuxt dev --debugmodu: Hydration farklarını satır bazında gösterir.- Vue Devtools: SSR’den gelen HTML ile client-render arasındaki farkları inceleyebilirsin.
Çözüm Önerileri
| Sorun | Çözüm |
|---|---|
| Zaman veya tarih tabanlı render | onMounted içinde hesapla |
| Random değerler | SSR yerine client’ta oluştur |
| window/document erişimi | process.client veya onMounted kullan |
| API verisi tutarsızlığı | useAsyncData için benzersiz key |
| Şartlı render farkı | SSR ile aynı state’ten başla |
Bonus: isHydrating & onHydrated
Nuxt 3’teuseNuxtApp() üzerinden hydration durumuna erişebilirsin.const nuxtApp = useNuxtApp()
nuxtApp.hook('app:hydrated', () => {
console.log('Hydration tamamlandı!')
})
Bu event, Vue DOM eşleşmesini tamamladığında tetiklenir. Özellikle performans ölçümlerinde faydalıdır.Hydration ile Suspense Uyumsuzluğu
<Suspense> bileşeni asenkron içerikler barındırdığında, SSR sırasında beklenmedik içerik farkları oluşabilir. Bu durumda fallback içeriği SSR’de, asıl içerik ise client tarafında gösterilir.✅ Çözüm: Suspense altındaki async bileşenlerin suspensible: false olarak ayarlanması veya v-if="hydrated" kontrolüyle yüklenmesi.
Sonuç
Hydration Mismatch hataları korkutucu görünse de, sebebi her zaman aynı: Server ve client farklı DOM üretmiştir. Çözüm ise tahmin edilebilir: tarayıcıya özel kodu yalnızca client tarafında çalıştır.Kısaca:
window,document,Math.random(),Date()= dikkat!- SSR ve client state’leri aynı olmalı.
onMountedveprocess.clientkurtarıcıdır.