TransitionGroup fails to hydrate when child has v-if
Vue version
3.2.39
Link to minimal reproduction
https://sfc.vuejs.org/#__SSR__eNp9j81uwyAQhF8F7TmAmpxq0Sg99QV65ELiTUNjYLVgV1Xkdy9urCg/Um4wM7vf7AneidTQIzRgCgbqXMG1jUKYT3Yx++JT/ODU01ls/SCO+PtmwVkQg/T7+ty7LqOF9e7gu1a8GF1T5zjN4e3FXRo9r9KPAKMvFWABPlDiIoMj9Z1TrA1P06CdjWyhEf/KpNUTpr+FQymUG637SMcvtUtBb6qnuY/FB5RtCpuVWqrVa22Zy7WuMAe55fSTkSvRwuJqua7igCwZY4uM/BR2l70B3nkP0Ik52jjC+AeJqoxU
Steps to reproduce
No steps necessary, check the rendered output and the warning
What is expected?
The children inside <TransitionGroup> are rendered just once and the hydration succeeds
What is actually happening?
The child in <TransitionGroup> is rendered twice and a hydration warning appears
System Info
No response
Any additional comments?
I suppose that happens because <TransitionGroup> skips comment nodes (they can't be transitioned and have no boundingClientRect), so it expects the actual child but finds a comment node, "fixes" that by adding the expected child node even though it's already there, just one position behind
I found that this is because the SSR side allows comment nodes in the TransitionGroup, while on the client side it skips the rendering of comment nodes, so it causes a mismatch warning during hydration.
Here is the source code snippet that skips the rendering of comment nodes:
https://github.com/vuejs/core/blob/5381abc0571e58a9be6cf482dc50c8db8300f86c/packages/runtime-core/src/components/BaseTransition.ts#L502-L505
Yup! Looking into it atm.
I think one approach would be to pass true for the keepComment arg, another to change the generated SSR code so it doesn't output the comment HTML anymore (_push('<!---->')).
I suspect the first option causes issues because then the comment node is passed to setTransitionHooks(), but maybe the second works? Otherwise the TransitionGroup might need some additional checks, let's see.
Is there any workaround available for this issue?
For me it not only happens with v-if child, but also when you have a slot in a reusable transition component (as mentioned in this closed issue https://github.com/vuejs/core/issues/6922):
https://sfc.vuejs.org/#__SSR__eNp9kctqwzAQRX9FaJNNLEFbKBg1pHTRH+hSGz+mjRrrwUh2KcH/3pGTJk4M2c3cOeiO7hz4awhi6IGXXMUGTUgsQurDRjtjg8fE3rwN7BO9ZSshc5PxlXZKHnkiqUlgQ1cloI4xlbGporo1A9vD74vmleabZme6llVKkrwk6jNRXwg1uVKp5NmFr/lxvcJWQXxH7+gDh0zr0yBqXrJJyRqtnHvNdymFWErZu7D/Eo23ckszib1LxkLRert9FA/i6ZnsY5rrAqItavQ/EZAcNV/PHpckDoAFgmsBAe+a3bBXhjezhWn2HLUbKYD/W+TTXcf/gZWLJhnv3tFPt5xyjp1PTJ4yXTLzeMc/Dtm2iA==
@LiamMartens
Is there any workaround available for this issue?
Replacing the v-if with a v-for should work:
<!-- error -->
<p v-if="shouldShow">Hello!</p>
<!-- works -->
<p v-for="_ in shouldShow ? [1] : []">Hello!</p>
I've just run into the same issue using Nuxt.
The workaround I found was simply providing a v-else condition to render a self-closing empty element in case the v-if condition returns falsy which seems to solve the comment problem
<p v-if="computedProperty">{{ computedProperty }}</p>
<div v-else />
This works for me :
<component :is="component" tag="div">
....
....
</component>
const mounted = ref(false);
const component = computed(() => (mounted.value ? TransitionGroup : 'div'));
onMounted(() => {
mounted.value = true;
});
It's been a year, Can we consider this now? @sodatea
This worked for me too, had to make the element hidden so it doesn't mess with my styling,
<div class="hidden" v-else />
~I think this is also happening when using slots that are empty. On the server nothing is rendered, but on the client and empty comment is rendered.~ edit: this seems to only happen in nuxt, and not in vue with vite. I will open an issue there.
Hey, I just experienced the same issue.
I only had a few child elements in the TransitionGroup, so I decided to use v-show directive instead of v-if.
This seems to have resolved the HM issue in my case and kept the intended animation.
Hope this helps!
For me it not only happens with v-if child, but also when you have a slot in a reusable transition component (as mentioned in this closed issue #6922):
This doesn't reproduce (anymore?). But v-if causes hydration issues even in the newest release of Vue. I've rebased my PR to get rid of the conflicts