nuxt: [Vue warn]: Hydration node mismatch:
Environment
- Operating System:
Linux - Node Version:
v14.18.1 - Nuxt Version:
3.0.0-27243104.5e903ae - Package Manager:
Yarn - Bundler:
Vite - User Config:
meta,buildModules - Runtime Modules:
- - Build Modules:
nuxt-windicss@2.0.2
Describe the bug
A warning occurs in console when i add a @click attribute in html tag
[Vue warn]: Hydration node mismatch:
- Client vnode: i
- Server rendered DOM: <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
at <Navbar>
at <AsyncComponentWrapper>
at <Default name="default" >
at <AsyncComponentWrapper name="default" >
at <NuxtLayout key=0 name=undefined >
at <RouterView>
at <NuxtPage>
at <App>
at <Root>
Reproduction
.
Additional context
<template>
...
<a @click="menu"><i data-feather="menu"></i></a>
</template>
<script setup>
const menu = () => {
console.log('menu')
}
</script>
Logs
No response
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 23 (4 by maintainers)
@henningko
This is a fairly common hydration mistake. Making these mistakes comes from a misunderstanding of how SSR and hydration actually work.
So, I’ll try to explain briefly.
Your code:
When
Nuxtdoes anSSRfor this page, it generates anHTMLstructure:Because
isAuthenticatedwill always befalsebecause the work happens on the server, and there are nocookieson the server, no tokens, nothing to do with authentication.Next,
Nuxtwill pass this code to the client asHTMLandJS.But the trick is that the
HTMLstructure is now “dead”. That is, it is not connected in any way with the state, there are no listeners on it. It’s just non-interactiveHTML. The process of “revitalizing” theHTMLstructure is called “hydration”.When hydrated,
Vuedoesn’t re-render theHTML, but rather “picks up”.The pickup goes like this:
Vuehas avirtual component tree (virtual DOM)and just recursively goes through it, connecting the realDOMnode to thevirtual DOMnode along the way.The
v-ifdirective doesn’t just addstyle="display: none"(as thev-showdirective does), but completely cuts the node physically from theDOM.Also, the setup code is executed BEFORE hydration or rendering. That is, the authorized client receives true in
v-if, unlike the server.So if the client is authorized, when
Vuetries to hydrate your component, it will try to synchronize the nextvirtual DOM:With the actual (which came from the server):
Which will cause a sync error.
As a quick fix, consider using
v-showinstead ofv-if. But this solution is bad for several reasons. For example, there is nov-show-else. Secondly,style="display: none"will solve the hydration problem, but if you have a lot of elements that depend on authorization, then usingv-showwill significantly increase both the number ofDOMnodes, which will slow down the page, and the virtual sizeDOM, each node of which is served by reactivity.As the right solution, it is worth considering the migration of authorization to the client in full:
The
onMountedhook only runs on the client.Even better, wrap your component in a custom
<ClientOnly></ClientOnly>tag.Hope it’s a little clearer.)
tracking need for
<client-only>at https://github.com/nuxt/nuxt.js/issues/11884Trying to wrap my head around this…:
Which probably stems from a problem with my if/else, as the form referenced up top is the form shown if
isAuthenticated()fails:useAuth.tswhereby
userSessionis set bysupabase.auth, and a subsequent request to/api/authensures that a cookie is being set so that Nuxt’s API can make requests to supabase on behalf of the user. Full repo: https://github.com/henningko/briefxyz/tree/api New to Nuxt3/SSR/Supabase and biting off more than I can chew, probably, so please don’t hold back with criticism 😃Environment
macOSv17.4.03.0.0-27398533.8edd481YarnVitebuildModules,RuntimeConfig-nuxt-windicss@2.2.2I had the same issue. I tried wrapping the div with
ClientOnlyand it works. I don’t see the warning in the console log anymore.I had this problem with font-awesome icons. My workaround was this:
This waits for the page to finish, i.e. the content to be present client-side.
If you want to use the whole feather-icons package, I think it’s easiest to implement a component which renders the icon to SVG, like so:
which could be used as follows
see https://codesandbox.io/s/happy-kare-hoy65?file=/src/components/FeatherIcon.vue:0-536
There are also other packages like vue-feather, but IMHO those packages often provide features which are not always required in every project…
@sanjarbarakayev,
Let’s simplify the context to focus on the problem. Let’s look at the following problematic component
pages/page.vue:/app.vuepages/page.vueWhen reloading the page, sometimes (with a ~50% chance) a hydration error will occur. About the same as yours.
You need to synchronize the
loadingvalue on the server and client.So, there are three ways to implement this synchronization, depending on your context:
ClientOnly
Disable SSR altogether for the problem area.
pages/page.vueonMounted
pages/page.vueYou might figure out that the code is executed differently on the client, but in general the method looks like this. Essentially the same as ClientOnly, but your
<div>loading</div>will be rendered on the server. Do you need your<div>loading</div>in SEO? This is a big question.load only on client
In this case, you ensure that
notificationStore.loadingon the server is always the same as in setup on the client. This is achieved, for example, this way:/app.vueNow throughout the entire project you won’t have hydration problems due to
notificationStore.loading.References:
Which to choose?
Without knowing your context, I suspect that
notificationStoreis needed to display client notifications. In this case, rendering loaders will be useless for SEO, and the SEO bot will always be without an account, i.e. it will see something like: ‘You have no notifications.’In this case, the option with
ClientOnlyis suitable for you.If you need SEO to display notifications, then I recommend the
load only on clientoption.This works for FontAwesome Items in Nuxt 3 , Thanks for sharing
I got this error by using conditional logic inside a named slot in a base container template, like this:
Note how I thought like others that it might have been the feather library (loading in a separate component) that was the issue. Negative, removing that library had no effect. The issue was having the v-if=“loading” inside the named slot. Upon removing the conditional logic from the Base template and moving that up to the parent template, no hydration error. Note in the above, I’m receiving loading as a prop. I may have been wrong here in the first place regarding conditional rendering inside a slot but still thought this was worth sharing in case someone else encounters the same. No v-if / conditional rendering inside named slots (or at least not in this example).
Edit: More testing, identified that I can add conditional logic on BaseLoadingFeather component that loads the feather library without use of onNuxtReady if the loading slot is wrapped in a ClientOnly component. Thought that I had tested that specific (seems no), passing without hydration issues like this inside a base component template. Sharing in case this helps someone.
Use case is like this:
You should pay attention to the warnings and add some handling on the client side to avoid updating the variable until your app has mounted. You can use
onNuxtReadycomposable in the next Nuxt release to do so: https://github.com/nuxt/framework/pull/9478.