Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client-only content doesn't render in component's slot #105

Open
DamianGlowala opened this issue Feb 15, 2021 · 10 comments
Open

Client-only content doesn't render in component's slot #105

DamianGlowala opened this issue Feb 15, 2021 · 10 comments

Comments

@DamianGlowala
Copy link

Original issue reported at nuxt/nuxt.js#8579.

The content inside <client-only> tag doesn't render when the tag is placed in a component's slot of another component's slot, as shown in the following reproduction:
codesandbox

@egoist any chance you could look into this issue please? I can see the repo hasn't been updated in a while but this missing functionality is crucial for some use-cases, when client-only tag is the only way to go in the nested slots structure :/

@mrleblanc101
Copy link

mrleblanc101 commented Mar 3, 2021

I have this exact issue, I'm surprised nobody faced this issue before.
v-calendar (v-date-picker) is not SSR compatible, so I wrap in client-only.
This cause an SSR error:

vue.runtime.esm.js?2b0e:6427 Mismatching childNodes vs. VNodes: NodeList [comment], NodeList [VNode]
I thought the problem was with v-calendar, but I still get the error if I only use client-only and standard HTML inside.

Here is my exact component.

<template>
    <v-widget :name="$t('agenda')">
        <div>
            <client-only>
                <div class="flex-container">
                    <v-date-picker v-model="selectedDate" :attributes="attributes" is-required />
                    <div class="events">
                        [...]
                    </div>
                </div>
            </client-only>
        </div>
    </v-widget>
</template>

And here is v-widget:

<template>
    <div class="widget">
        <h2 class="widget-name">{{ name }}</h2>
        <slot></slot>
    </div>
</template>

@mrleblanc101
Copy link

From my understanding, Vue think it has to render a comment <!-- --> (the SSR placeholder), but on the client side, it realise it need to render a <div> which trigger the missmatch.

@mrleblanc101
Copy link

Wrapping my slot in a <div> seems to fix the issue, but it's not actually possible in my case because of how my flex layout is made.

<template>
    <div class="widget">
        <h2 class="widget-name">{{ name }}</h2>
        <div>
            <slot></slot>
        </div>
    </div>
</template>

@mrleblanc101
Copy link

mrleblanc101 commented Mar 4, 2021

My problem was with Nuxt component autoload/discovery.
Replacing this with a plugin that manually import my global components fixed the problem.

components: [
    { path: '@/components/global', global: true },
],

@akashrajum
Copy link

akashrajum commented Aug 4, 2021

Hey, I'm also stuck with a similar issue, as you can see in this code sandbox -- https://codesandbox.io/s/vue-horizontal-calendar-nuxt-demo-myytd?file=/pages/index.vue I am able to render component normally but if I pass it inside a template slot it's not rendering.

vue-horizontal-calendar is the component, I have declared twice but the one outside template is the only one rendering

@akashrajum
Copy link

One tiny workaround I found for now is by removing scoping to the template and using the slot where we have to pass the component as the default slot and template. So this basically - https://codesandbox.io/s/vue-horizontal-calendar-nuxt-demo-forked-hvx9e?file=/pages/index.vue

@MarineLB
Copy link

Had the same issue that I worked around by wrapping the entire component in the client only instead of the slot.

Initially, triggering the issue

<my-component>
   <client-only>{{ foobar }}</client-only>
</my-component>

The current fix, not ideal but works in our case

<client-only>
    <my-component>{{ foobar }}</my-component>
</client-only>

@Razz21
Copy link

Razz21 commented Feb 10, 2022

Not perfect, but I believe its possible to overcome this issue by rewriting <client-only> to a regular, stateful component. You lose of course some benefits or functional components (rendering multi root elements or performance), but at least renders slot content properly.

  render(h) {

    if (this._isMounted) {
      return this.$slots.default;
    }
    this.$once('hook:mounted', () => {
      this.$forceUpdate();
    });

    if (this.placeholderTag && (this.placeholder || this.$slots.placeholderSlot)) {
      return h(
        this.placeholderTag,
        {
          class: ['client-only-placeholder']
        },
        this.placeholder || this.$slots.placeholderSlot
      )
    }

    return this.$slots.default && this.$slots.default.length > 0 ? this.$slots.default.map(() => h(false)) : h(false)
  }

@mrleblanc101
Copy link

mrleblanc101 commented Oct 31, 2023

Just faced this issue again in 2023.
The problem is not with slot in general, but with named slots.

For exemple, this will work since it's a default slot (the <template> is not even necessary as eslint points-out)
Screenshot 2023-10-31 at 5 47 11 PM

But this won't work for some reason, still a default slot, but I added #default on the template.
Screenshot 2023-10-31 at 5 46 13 PM

@mrleblanc101
Copy link

What seem to have worked for me was to use slot="slotName" instead of v-slot:slotName or the #slotName shorthand

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants