DEV Community

Cover image for How to Fix Vue Hydration Mismatch
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

How to Fix Vue Hydration Mismatch

When working with Vue or Nuxt, one of the most confusing and frustrating issues you might encounter is the hydration mismatch warning.
This problem usually appears when your app behaves differently on the server and the client, causing Vue to throw warnings or — worse — break interactivity after hydration.

In this article, we’ll explore:

  • What the hydration mismatch problem actually is
  • How it manifests in Vue and Nuxt applications
  • Why it’s dangerous to ignore it
  • Common real-world causes
  • Best practices to avoid it

Enjoy!

🤔 What Is Hydration Mismatch in Vue?

Hydration is the process where Vue takes the static HTML generated by the server (in SSR or SSG apps) and “hydrates” it with reactivity and event listeners on the client.

A hydration mismatch occurs when the HTML rendered on the client differs from the one generated on the server.
This can lead to Vue displaying warnings such as:

Hydration completed but contains mismatches.
Enter fullscreen mode Exit fullscreen mode

or

Text content does not match server-rendered HTML.
Enter fullscreen mode Exit fullscreen mode

These warnings usually appear in the browser console and are often the first indication that something went wrong between your server-side and client-side rendering.

🟢 Why Hydration Mismatches Are Dangerous

Ignoring hydration mismatches may seem harmless at first — the page still renders, right? But under the hood, things can go wrong fast.

Here’s why it’s important to fix them:

  • ⚠️ Broken interactivity – Some event listeners may not attach properly, breaking buttons, forms, or modals.
  • 🎭 Flickering UI – When the client re-renders elements differently, the user may see visible layout jumps.
  • 💾 Inconsistent state – Your app’s initial state might differ between the server and client, causing unpredictable bugs.
  • 🧩 SEO and analytics issues – If the server sends different markup than what’s actually used on the client, crawlers might misinterpret your content.

In short, hydration mismatches break the trust between what the user sees and what Vue believes is on the screen.

🟢 Common Scenarios That Cause Hydration Mismatches

Let’s go through some common real-world cases where hydration mismatches happen in Vue and Nuxt projects.

1. Using Browser APIs During Server Rendering

Vue’s server-side rendering runs in a Node.js environment, where objects like window, document, or localStorage don’t exist.

If you use them directly during SSR, the server-rendered HTML will differ from what the client produces later.

Example:

<template>
  <div>{{ window.innerWidth }}</div>
</template>
Enter fullscreen mode Exit fullscreen mode

Fix:

<template>
  <div v-if="width">Width: {{ width }}</div>
</template>

<script setup>
import { onMounted, ref } from 'vue';

const width = ref(null);
onMounted(() => {
  width.value = window.innerWidth;
});
</script>
Enter fullscreen mode Exit fullscreen mode

2. Random Values or Non-Deterministic Output

If you generate random numbers, timestamps, or IDs during render, your HTML will differ between SSR and CSR.

Example:

<template>
  <p>User ID: {{ Math.random() }}</p>
</template>
Enter fullscreen mode Exit fullscreen mode

Fix:
Use a deterministic ID generated on the server and passed as a prop or from a store/state.

3. Data Fetched Only on Client Side

If you rely on API calls made only in onMounted() and don’t render placeholder data on the server, Vue will try to hydrate with empty HTML and then replace it, causing mismatches.

Fix:
In Nuxt, use useAsyncData() or useFetch() so that data is fetched both server-side and client-side consistently:

const { data } = await useAsyncData('users', () => $fetch('/api/users'));
Enter fullscreen mode Exit fullscreen mode

4. Conditional Rendering with Non-SSR-Safe Logic

Sometimes, UI elements depend on conditions that differ between SSR and client rendering, such as theme detection, cookies, or viewport checks.

Example:

<template>
  <div>{{ isDark ? '🌙 Dark mode' : '☀️ Light mode' }}</div>
</template>

<script setup>
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
</script>
Enter fullscreen mode Exit fullscreen mode

Fix:
Handle it after hydration or through a plugin that syncs theme preference on both sides.

✅ Best Practices

Here are some key strategies to avoid hydration mismatches in Vue and Nuxt:

  1. 🧠 Avoid using browser-only APIs during SSR — wrap them in onMounted() or if (process.client) checks.
  2. 🕐 Use deterministic rendering — don’t generate random or time-based values in templates.
  3. 🧩 Use useAsyncData or useFetch in Nuxt to synchronize data between server and client.
  4. ⚙️ Keep server and client state aligned — share initial data through props or stores.
  5. 🎨 Be cautious with conditionals — make sure elements rendered on the server also exist on the client (even if hidden).
  6. 🔍 Test hydration locally — run your app in SSR mode and check the browser console for mismatches early.

📖 Learn more

If you’d like to learn more about Vue, Nuxt, JavaScript, or other modern web technologies, check out VueSchool by clicking this link or the image below:

Vue School Link

It covers essential concepts that help you build and debug real-world Vue or Nuxt applications effectively.

🧪 Advance skills

A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you’re advancing your career or switching paths, it’s a smart step toward success.

Check out Certificates.dev by clicking this link or the image below:

Certificates.dev Link

Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more!

✅ Summary

A hydration mismatch occurs when your Vue or Nuxt app renders different HTML on the server and the client. While it may look harmless, it can lead to broken interactivity, visual flickers, and inconsistent state.

By following best practices — avoiding browser-only logic on the server, keeping rendering deterministic, and syncing data properly — you can prevent hydration issues and ensure your Vue app works flawlessly across environments.

Take care!
And happy coding as always 🖥️

Top comments (0)