Hydration mismatch xảy ra khi HTML do server render khác với những gì Vue client muốn render — Vue sẽ warning và re-render toàn bộ component.
Nguyên nhân phổ biến:
1. Browser-only APIs trong setup: localStorage, window, document không tồn tại trên server
2. Random/Date values: Math.random(), Date.now(), new Date() cho kết quả khác nhau
3. Conditional dựa trên user agent/cookies: server không có context này
4. Third-party directives chỉ chạy client-side
Fixes:
// 1. Dùng <ClientOnly> component trong Nuxt
<ClientOnly fallback-tag="span">
<ComponentThatUsesWindow />
</ClientOnly>
// 2. import.meta.client (Nuxt 3 idiom; process.client là Nuxt 2 legacy)
const count = ref(import.meta.client ? localStorage.getItem('count') : null)
// 3. onMounted cho browser-only code
onMounted(() => {
// Safe — only runs on client
state.value = localStorage.getItem('key')
})Lưu ý: Vue/Nuxt không có suppressHydrationWarning attribute (đó là React).
Để intentional mismatch dùng <ClientOnly> hoặc :key trick để force re-render.
Hydration mismatch occurs when server-rendered HTML differs from what Vue client tries to render.
Common causes:
1. Browser-only APIs in setup: localStorage, window, document don't exist on server
2. Random/Date values: Math.random(), Date.now() return different values
3. User agent/cookie-based conditionals
Fixes:
// 1. <ClientOnly> wrapper in Nuxt (Vue/Nuxt has no suppressHydrationWarning — that's React)
<ClientOnly fallback-tag="span">
<ComponentThatUsesWindow />
</ClientOnly>
// 2. import.meta.client (Nuxt 3 idiomatic; process.client is Nuxt 2 legacy)
const val = ref(import.meta.client ? localStorage.getItem('key') : null)
// 3. onMounted for browser-only code
onMounted(() => {
state.value = localStorage.getItem('key')
})