Vuejs Error: The client-side rendered virtual DOM tree is not matching server-rendered
Asked Answered
Q

22

94

I am using Nuxt.js / Vuejs for my app, and I keep facing this error in different places:

    The client-side rendered virtual DOM tree is not matching server-rendered content. 
This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. 
Bailing hydration and performing full client-side render.

I would like to understand what is the best way to debug this error? Is their a way I can record/get the virtual DOM tree for client and server so I could compare and find where the error lies?

Mine is a large application and manually verifying is difficult.

Quibbling answered 18/12, 2017 at 5:29 Comment(1)
inspect the issue: blog.lichter.io/posts/vue-hydration-error/…Ellipsis
C
236

Partial answer: with Chrome DevTools, you can localize the issue and see exactly what element caused the issue. Do the following (I did that with Nuxt 5.6.0 and Chrome 64.0.3282.186)

  1. Show DevTools in Chrome (F12)
  2. Load the page that causes "the client-side rendered virtual DOM tree..." warning.
  3. Scroll to the warning in DevTools console.
  4. Click at the source location hyperlink of the warning (in my case it was vue.runtime.esm.js:574).
  5. Set a breakpoint there (left-clicking at line number in the source code browser).
  6. Make the same warning to appear again. I'm not saying it is always possible, but in my case I simply reloaded the page. If there are many warnings, you can check the message by moving a mouse over msg variable.
  7. When you found your message and stopped on a breakpoint, look at the call stack. Click one frame down to call to "patch" to open its source. Hover mouse over hydrate function call 4 lines above the execution line in patch. Hyperlink to the source of hydrate would open.
  8. In the hydrate function, move about 15 lines from the start and set a breakpoint where false is returned after assertNodeMatch returned false. Set the breakpoint there and remove all other breakpoints.
  9. Make the same warning to happen again. Now, when breakpoint is hit, execution should stop in the hydrate function. Switch to DevTools console and evaluate elm and then vnode. Here elm seem to be a server-rendered DOM element while vnode is a virtual DOM node. Elm is printed as HTML so you can figure out where the error happened.
Cutlerr answered 9/3, 2018 at 21:21 Comment(3)
Nuxt 5.6.0, You live in the future?Raffarty
A quicker way to access the hydrate function execution is to expand the error in the console area of Chrome dev tools and you can see it in the list. Simply click the link after the @ symbol of the same line. e.g. hydrate @ commons.app.js:15934Blankly
I found that this blog posted an expanded explanation of this error, based on @Cutlerr answer and it actually helped me understand the problem. Hope this can helps someone else: blog.lichter.io/posts/vue-hydration-errorBresnahan
I
31

For me this error happened cuz get Array list in AsyncData and rendered <tr> tags by v-for, i put v-for codes in <client-only> blocks and problem solved

Iy answered 22/4, 2020 at 21:34 Comment(4)
If your not using Nuxt you will need to install vue-client-onlyGarrik
Does this mean we cannot render table server side? and send the complete html to the browser? this really ruin the concept of SSR and SEO features of Nuxt. I get the same issue and can be resolved with <client-only> block but it is not the real fix I guessMisdemean
@Misdemean you can render tables server-side, as long as you make sure rows are wrapped in <thead>, <tbody> and <tfoot> tags (see MDN for reference on how to use these tags properly)Triparted
@FelixEve this one is no longer needed because it is baked into Nuxt.Cervicitis
A
26

This error can be really painfull to debug. In order to quickly get the element causing an issue edit node_modules/vue/dist/vue.esm.js and add the following lines :

// Search for this line: 
function hydrate (elm, vnode, insertedVnodeQueue, inVPre) {
    var i;
    var tag = vnode.tag;
    var data = vnode.data;
    var children = vnode.children;
    inVPre = inVPre || (data && data.pre);
    vnode.elm = elm;

    // Add the following lines: 
    console.log('elm', elm)
    console.log('vnode', vnode)
    console.log('inVpre', inVPre)
    // ...


You will get in the console the failing node.

Annatto answered 6/5, 2020 at 10:46 Comment(3)
It is the easiest way to debug this issue. can find out the element responsible for any warning or error. thanks a lot.Flor
Thanks for you answer, did you perform any additional actions except editing this file and starting nuxt (probably yarn dev). Does not work for me, old file is used according to Sources in Chrome(Apgar
@BogdanTimofeev I did not perform any addition actions. I was using Vue without Nuxt. For Nuxt it may be an other similar file.Annatto
C
22

There are a lot of ways of fixing this issue, but most of them are not actual fixes, just hacky band-aids. To note a few:

  • wrap it into <client-only> tags, beware of some important details tho
  • using a v-show instead of a v-if
  • trying to hack some lifecycles
  • etc...

I highly recommend reading this gorgeous article written by Alexander Lichter

https://blog.lichter.io/posts/vue-hydration-error/

He'll explain you that you should diagnose why this happens and fix the actual issue.
Basically each time something is different from what was generated on the server and what is available when done hydrating on the client will cause this error.

Some of which are:

  • invalid HTML (having a block element inside of a <p>, same goes for an a tag nested into another, etc...)
  • 3rd party scripts messing around with your components
  • different state on server vs client
  • any random is risky (new Date() for example)
  • any page related to authentication

I highly recommend reading the article to understand in Alexandre's own words how to handle this kind of issue. If you're in a hurry you could always use one band-aid fix but try to actually fix the issue for the best performance and to keep the code clean.

Cervicitis answered 15/6, 2021 at 0:8 Comment(0)
L
13

I had the same issue as of nuxt version 2.14.0 while implementing vue-particles package. The fix was to surround the tags with no-ssr and it fixed the issue.

EDIT:
Updated variant of the solution (if Nuxt version is above 2.9.0)

<client-only>
  <vue-particles>
  </vue-particles>
</client-only>

Old solution:

<no-ssr>
  <vue-particles>
  </vue-particles>
</no-ssr>
Longeron answered 28/8, 2020 at 11:42 Comment(0)
G
12

Thanks to budden73's answer, I did a little improvement on the debug process.

  1. Open dev tool
  2. click on the warn message, and click on the first line of the warn message, you will be directed to the Sources panel, with a file name vue.runtime.esm.js?xxxx

enter image description here

  1. ctrl+f to search the above file for assertNodeMatch, not the function, but like:
    if (process.env.NODE_ENV !== 'production') {
      if (!assertNodeMatch(elm, vnode, inVPre)) {
        return false
      }
    }
  1. Add a break point at the line return false

enter image description here

  1. Refresh the page, and the breakpoint will be triggered.
  2. At the right side of the Sources panel, Under Scope->Local, click on the elm element, you will be directed back to the Elements panel.

enter image description here

  1. The above element is the client side rendered element, compare with your code to see the difference.

If you can't find the source of the bug, the brutal way to fix it is using nuxt's <client-only> tag.

Another likely brutal way is described here. Add an isHydrate variable which default is false, set to true in mounted hook, and render the element after the variable set to true.

Giffin answered 23/3, 2022 at 11:21 Comment(1)
Great advice. A good tip for the future.Jordanson
V
6

For Nuxt version above 2.10 it doesn't need to install nothing, just use the default component <client-only> as mentioned https://nuxtjs.org/api/components-client-only/.

Very answered 19/7, 2020 at 19:9 Comment(0)
W
6

Check the previous warning:

In "nuxt": "^2.12.2", You can spot the cause easily from the previous warning.

enter image description here

In my case:

Incorrect

<nuxt-link to="/game42day">
  <a>Game For Today</a>
</nuxt-link>

Correct:

<nuxt-link to="/game42day">
  Game For Today
</nuxt-link>
Wychelm answered 9/8, 2020 at 10:40 Comment(0)
V
4

Turns out, in my case, I had HTML comment tags , which was causing this stupid, annoying error. Took me too long to figure it out but in case it helps someone.

Violation answered 18/6, 2020 at 19:55 Comment(0)
L
4

If you're rendering a component conditionally with v-if, then you have two options to solve the problem:

The first one is wrapping the element in <no-ssr></no-ssr> tag.

The second approach is replacing v-if with v-show, here is the link to Vue docs.

Lieu answered 20/12, 2020 at 17:20 Comment(0)
U
2

In my case I had to change this:

<v-expansion-panel-header v-text="name" />

to this:

<v-expansion-panel-header>{{ name }}</v-expansion-panel-header>
Uredium answered 21/9, 2020 at 17:56 Comment(0)
M
2

I also get many errors due to this problem. I list two cases I often encounter, hope can help you.

  • With vuetify button, when you create a common component, you should use: <v-btn>{{text}}</v-btn>. Example:
<template>
    <v-btn
      :width="width"
      :color="color"
      :class="[rounded ? 'rounded-pill' : 'rounded-lg',textColor]"
      v-on:click="onClick"
      elevation="0"
      :outlined="outlined"
      :type="type"
      :name="name"
      :form="form"
      :disabled="disabled"
      v-bind="$attrs"
    >{{ text }}</v-btn>
</template>
  • Don't use v-html with <p> tag. Not use: <p v-html='html'></p>. Use: <div v-html='html'></div>.

Besides, if you use <client-only></client-only>, this problem is definitely solved, but if you need to SEO page or show google ads, it is not good solution.

Madness answered 15/9, 2021 at 8:48 Comment(0)
S
1

See here for an example of how to deal with integrations (e.g. Google Analytics or FB Pixel) that modify the DOM. Basically create a plugin and exclude from SSR.

https://nuxtjs.org/faq/ga

Sartin answered 16/3, 2019 at 21:19 Comment(0)
D
1

Ok this is going to sound silly. I tried a bunch of different solutions for about 15 mins such as restarting the server and deleting the .nuxt directory but I was too lazy to use @budden73's big brain solution. What ended up working for me was simply restarting my computer, give it a shot.

Disperse answered 4/1, 2020 at 15:54 Comment(0)
L
1

What I have found so far from observation is that when you are using third party packages like jQuery (specially), they sometimes inject html tags into the dom. So Vue/Nuxt looses track of the dom tree and starts complaining.

I was having the same problem and after a while I removed all jQuery and replaced jQuery functionality with Vuejs and those error were all gone.

Ladder answered 25/2, 2020 at 4:42 Comment(0)
C
1

What about:

extend (config, ctx) {
  config.resolve.symlinks = false
}

See this [Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content ( Nuxt / Vue / lerna monorepo )

Contort answered 28/10, 2020 at 12:27 Comment(0)
D
1

Now that you found the code causing the problem, the first thing you should do is to verify that your markup (possibly coming from an API) is valid. Code like <p><p>Text</p></p> is not valid because a p element doesn’t allow other block elements (like a paragraph tag) inside.

Be aware, that tags are not allowed to have block level elements like <div> or <p> as children. These <span> tags are used default tag for Vue’s transitions though. You can change that though via <Transition tag="div">.

Desireah answered 3/8, 2022 at 12:14 Comment(1)
This is 100% match qoute from blog.lichter.io , so should be marked as qoute ;)Pinochle
C
1

In my case this problem was caused by markdownit module, I solved it by changing the html markup used with v-html. I was with <p> at the beginning and I ended with <div>.

I have some <p> in my v-html render (with $md.render()) so take care if you have same problems with different markups.

Cila answered 18/1, 2023 at 18:55 Comment(0)
I
0

Check if have used any block-level element inside the inline element.

for example: inside , inside

If you have used an HTML table make sure you have used the tag

Iridize answered 8/7, 2021 at 6:40 Comment(0)
B
0

In my case, I changed my codes from

<p v-html="$md.render(post.content)"></p>

to

<p>{{ $md.render(post.content) }}</p>
Bailey answered 5/10, 2021 at 10:57 Comment(0)
H
0

You can also use the Chrome local Overrides function to add the console.logs:

        var i;
        var tag = vnode.tag, data = vnode.data, children = vnode.children;
        inVPre = inVPre || (data && data.pre);
        vnode.elm = elm;

    console.log('elm', elm)
    console.log('vnode', vnode)
    console.log('inVpre', inVPre)
      
        if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {
            vnode.isAsyncPlaceholder = true;
            return true;
        }

Add local overrides here: [1]: https://i.stack.imgur.com/jDbab.png Then you can edit the file. First search for function hydrate in the search tab.

Hypothesize answered 9/3, 2023 at 10:25 Comment(0)
P
0

In my case I got that error because I was using an <a> tag inside a <NuxtLink>

I show you the example:

<NuxtLink to="/services">
    <a class="main-content-left-content-more_link ml-30 pointer m-ml-0">
        My link
    </a>
</NuxtLink>

The solution is to remove the <a> tag and only use <NuxtLink>

Petrosal answered 6/1 at 4:31 Comment(1)
This is an HTML semantic rule indeed (cannot have a link inside of a link), not even related to Vue/Nuxt.Cervicitis

© 2022 - 2024 — McMap. All rights reserved.