Applying dynamic slots to a a dynamic <component> results in SSR hydration errors
Vue version
^3.2.25
Link to minimal reproduction
Steps to reproduce
Attempt to wrap an existing component template (imagine this coming from a UI lib) without redefining all the slots. This existing component would have a customizable element using the dynamic <Component>
element.
The dynamic base component:
<template>
<component :is="element">
<slot />
<slot name="title"> Default value for the title slot. </slot>
</component>
</template>
<script>
'use strict';
export default {
props: {
element: {
type: String,
required: false,
default: 'button',
},
},
};
</script>
The extending component, intended to contain customizations;
<template>
<DynamicComponent element="strong">
<template v-for="(_, name) in $slots" v-slot:[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
</DynamicComponent>
</template>
<script setup>
import DynamicComponent from './Dynamic.vue';
</script>
Implementation example;
<ExtendedComponent>
<template #title> My custom title</template>
</ExtendedComponent>
What is expected?
Output in both SSR and on hydrate is the same, no errors
<strong>My custom title</strong>
What is actually happening?
Hydration mismatch, it appears the server-rendered version is not correct;
[Vue warn]: Property "name" was accessed during render but is not defined on instance.
[Vue warn]: Hydration node mismatch:
- Client vnode: Symbol("v-fgt")
- Server rendered DOM:
#text " Default value for the title slot. " (text)
at <Dynamic element="strong" >
at <Extend>
at <App>
Hydration completed but contains mismatches.
Note: After hydration, the correct content /is/ shown. But this is breaking behaviour on the server-side.
System Info
No response
Any additional comments?
First reported as a nuxt bug in #21915 but appears to be caused upstream in Vue SSR.
Thanks, not sure how that mistake crept into my project, I seem to remember having it the other way around and that not working either. But this seems to remedy the issue.
@FreekVR Another thing that'll make this component work is v-for="(name, index) in Object.keys($slots)"
Yeah, I had some cases where I had to do that, although I am not sure why. If you don't need the index (my eslint config doesn't like unused variables) then v-for="name in Object.keys($slots)
works even better :)