[Vue warn]: Hydration node mismatch with v-if

I get the error “[Vue warn]: Hydration node mismatch” when using the “v-if” condition in my NavigationHeader.vue component.

Here’s the technical stack used :

  • Vue.js v3
  • Nuxt.js v3
  • Nuxt UI

Hydration completed but contains mismatches.

[Vue warn]: Hydration node mismatch

I do understand that hydration is the process by which a Vue application takes server-side generated HTML content and transforms it into an interactive client-side application.

Below is the code snippet from the NavigationHeader.vue component causing the error. I’ve included two potential solutions that seem to solve the issue, although I’m not sure they’re appropriate in this situation. ⬇️

<script setup lang="ts">
const { isAuthenticated, logout } = useAuthentication();

const handleLogout = async () => {
  try {
    await logout();
  } catch (e) {
    console.error(e);
  }
};
</script>
<!-- Hydration node mismatch error. -->
<template>
  <BaseButton v-if="!isAuthenticated" label="Login" to="/login" />
  <BaseButton v-if="isAuthenticated" label="Logout" @click="handleLogout" />
</template>

<!-- Works because buttons are rendered client-side only. However, this option doesn't suit me because I want my button components to be rendered at the same time as the NavigationHeader.vue component. -->
<template>
  <ClientOnly>
    <BaseButton v-if="!isAuthenticated" label="Login" to="/login" />
    <BaseButton v-if="isAuthenticated" label="Logout" @click="handleLogout" />
  </ClientOnly>
</template>

<!-- Works because the component is rendered in the DOM but hidden with CSS. -->
<template>
  <div v-show="!isAuthenticated">
      <BaseButton label="Login" to="/login" />
  </div>
  <div v-show="isAuthenticated">
      <BaseButton label="Logout" @click="handleLogout" />
  </div>
</template>

I wonder what the best practice is for dealing with “[Vue warn]: Hydration node mismatch” errors ?