Subscribe on changes!

TransitionGroup fails to hydrate when child has v-if

avatar
Sep 21st 2022

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

avatar
Sep 22nd 2022

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

avatar
Sep 22nd 2022

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.

avatar
Feb 28th 2023

Is there any workaround available for this issue?

avatar
Mar 9th 2023

@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>
avatar
Mar 23rd 2023

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 />
avatar
Jul 25th 2023

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;
});
avatar
Aug 17th 2023

It's been a year, Can we consider this now? @sodatea

avatar
Sep 15th 2023

This worked for me too, had to make the element hidden so it doesn't mess with my styling,

<div class="hidden" v-else />
avatar
Sep 20th 2023

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.

avatar
Jan 11th 2024

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!

avatar
Mar 1st 2024

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):

sfc.vuejs.org/#__SSR__eNp9kctqwzAQRX9FaJNNLEFbKBg1pHTRH+hSGz+mjRrrwUh2KcH/3pGTJk4M2c3cOeiO7hz4awhi6IGXXMUGTUgsQurDRjtjg8fE3rwN7BO9ZSshc5PxlXZKHnkiqUlgQ1cloI4xlbGporo1A9vD74vmleabZme6llVKkrwk6jNRXwg1uVKp5NmFr/lxvcJWQXxH7+gDh0zr0yBqXrJJyRqtnHvNdymFWErZu7D/Eo23ckszib1LxkLRert9FA/i6ZnsY5rrAqItavQ/EZAcNV/PHpckDoAFgmsBAe+a3bBXhjezhWn2HLUbKYD/W+TTXcf/gZWLJhnv3tFPt5xyjp1PTJ4yXTLzeMc/Dtm2iA==

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