Subscribe on changes!

Applying dynamic slots to a a dynamic <component> results in SSR hydration errors

avatar
Jul 25th 2023

Vue version

^3.2.25

Link to minimal reproduction

https://stackblitz.com/edit/github-u4xmkj?file=src%2Fcomponents%2FDynamic.vue,src%2FApp.vue,src%2Fcomponents%2FExtend.vue,package.json

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.

avatar
Jul 25th 2023

@FreekVR

That v-for would be v-for="(name, _), and the error is gone.

stackblitz

avatar
Jul 25th 2023

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.

avatar
Jul 25th 2023

@FreekVR Another thing that'll make this component work is v-for="(name, index) in Object.keys($slots)"

stackblitz

avatar
Jul 25th 2023

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

avatar
Jul 25th 2023

Yeah, I had some cases where I had to do that, although I am not sure why.

It's because the $slots is a proxy object.