Vue is missing server side rendered DOM, doubling it and throwing hydration node mismatch

Question:

browser build and python (flask) backend. As far as I understand everything should work, the DOM is identical in both and doesn’t change after that, but vue ignores the server-side rendered DOM and generates it from scratch. What surprises me even more is the fact that it does not delete the server’s initial rendered DOM, but doubles it in exactly the same way.
How to make vue work with prerendered dom?

console message:

vue.esm-browser.js:1617 
[Vue warn]: Hydration node mismatch:
- Client vnode: Symbol(Comment) 
- Server rendered DOM: " " (text) 
  at <RouterView> 
  at <App>
Hydration complete but contains mismatches.

Minimal, Reproducible example:
on code pen.
My code is quite complex and messy so I isolated the bug to html and js only.

Asked By: Marcel

||

Answers:

This answer is explaining the use case with a Nuxt configuration but is totally valid for your code too.

The issue here being that you probably have:

  • some hardcoded HTML string
  • SSR content generated by Vue
  • client-side hydrated content by Vue

All of them can have the same content, they will still not behave properly if some of them need to overwrite the previous one.

If you make your markup like this

<div id="app">
  <h1></h1>
</div>

You will not have any issue because it will only keep Vue SSR + client-side Vue and it will be plugging itself to the app id.

If it was in another context, I would recommend disabling JS to try that one out or clicking on "View page source" with your mouse but you will not have a clean result on Codepen.

SSR is a broad topic with a lot of qwirky issues, I recommend that you try that with either Nuxt or at least an SFC Vue setup with Vite to get the full scope and an easier time debugging the whole thing: https://vuejs.org/guide/quick-start.html#creating-a-vue-application

Trying out in Codepen is nice but will add more sneaky things I’d say.
So don’t spend too much time trying to fix that in this context, try directly into your VScode editor with some Vue devtools for a full explanation of what’s happening.

Answered By: kissu

It turned out that the issue for me was formating…

It was working:

<div id="app">{{ server rendered html }}</div>

It was not:

<div id="app">
    {{ server rendered html}}
</div>
Answered By: Marcel