TransitionGroup fails to hydrate when child has v-if
Vue version
3.2.39
Link to minimal reproduction
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:
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.
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):
@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;
});
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