Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Vue: Pass Slots through from Parent to Child Components

Vue: Pass Slots through from Parent to Child Components

The Situation

  • We've got some components A, B and C which provide different slots.
    const A = {
      template: `<div><slot name="a">Default A Content</slot></div>`
    }
    
    const B = {
      template: `<div><slot name="b">Default B Content</slot></div>`
    }
    
    const C = {
      template: `<div><slot name="c">Default C Content</slot></div>`
    }
  • We have a wrapper component W. It receives one of A, B or C as a prop and renders it. W is not aware which slots are provided by either of them.
    Vue.component('W', {
      props: ['child'],
      template: `<component :is="child" />`
    })
  • We want to write a template that uses W but also use the slots offered by the respective wrapped component.

The Problem

Vue won't let us do the following:

<W :child="A">
  <div slot="a">Special A Content</div>
</W>

It just does nothing. Which is very correct, if you think about it: Slot a is expected to be a slot of W, not of the wrapped A.

See this jsFiddle

The Solution

We need to extend our W component's template to pass through any slots it is provided to its wrapped component.

It looks like this:

Vue.component('W', {
  props: ['child'],
  template: `<component :is="child">
  <slot v-for="(_, name) in $slots" :name="name" :slot="name" />
</component>`
})

Now the example will work just fine: jsFiddle

Bonus: Scoped Slots

Scoped slots like the following...

const D = {
  template: `<div><slot name="d" emoji="๐ŸŽ‰">Default D Content</slot></div>`
}

...are not handled by the extended template above. They're just ignored.

However, those can be managed in a very similar fashion to regular slots:

<template v-for="(_, name) in $scopedSlots" :slot="name" slot-scope="slotData"><slot :name="name" v-bind="slotData" /></template>

Include that alongside the slot passing from above, and our W component will just pass every slot and scoped slot it receives down to its child:

Vue.component('W', {
  props: ['child'],
  template: `<component :is="child">
  <slot v-for="(_, name) in $slots" :name="name" :slot="name" />
  <template v-for="(_, name) in $scopedSlots" :slot="name" slot-scope="slotData"><slot :name="name" v-bind="slotData" /></template>
</component>`
})

See it in action in this jsFiddle.

@yangjunjun

This comment has been minimized.

Copy link

@yangjunjun yangjunjun commented Mar 22, 2019

Cool.

@marcelo-rebello

This comment has been minimized.

Copy link

@marcelo-rebello marcelo-rebello commented May 17, 2019

Amazing, just what I need.
Thanks

@sanasus

This comment has been minimized.

Copy link

@sanasus sanasus commented May 29, 2019

Thanks!

@nntai

This comment has been minimized.

Copy link

@nntai nntai commented Jun 18, 2019

Thank you so much

@loilo

This comment has been minimized.

Copy link
Owner Author

@loilo loilo commented Jun 18, 2019

You're welcome @nntai. Also your comment was the first Gist notification I received per email, seems they have finally shipped those! โค๏ธ

@prestonbrown

This comment has been minimized.

Copy link

@prestonbrown prestonbrown commented Jul 2, 2019

This is very very helpful, works great!

@sebj54

This comment has been minimized.

Copy link

@sebj54 sebj54 commented Jul 16, 2019

Thank you for the snippet, really useful, especially with the extra information ๐Ÿ‘Œ

@karakoz

This comment has been minimized.

Copy link

@karakoz karakoz commented Aug 16, 2019

Thank You very much! It's really helpful.

@ElMatella

This comment has been minimized.

Copy link

@ElMatella ElMatella commented Aug 19, 2019

Very nice, helped me a lot, thanks for sharing this!

@mrtwinkler

This comment has been minimized.

Copy link

@mrtwinkler mrtwinkler commented Aug 30, 2019

Lifesaver!

@hiravebapu

This comment has been minimized.

Copy link

@hiravebapu hiravebapu commented Sep 6, 2019

LifeSaver man

@tecbeast42

This comment has been minimized.

Copy link

@tecbeast42 tecbeast42 commented Sep 11, 2019

Genius ๐Ÿ‘

@cardonaomar86

This comment has been minimized.

Copy link

@cardonaomar86 cardonaomar86 commented Sep 22, 2019

The scoped one saved my day, Thanks.

@Satchitananda

This comment has been minimized.

Copy link

@Satchitananda Satchitananda commented Nov 17, 2019

Saved half an hour for me to figure out this stuff! ๐Ÿ‘

@sebpalluel

This comment has been minimized.

Copy link

@sebpalluel sebpalluel commented Nov 22, 2019

Really handy for atomic design ! Thank you !

@ndtuan412

This comment has been minimized.

Copy link

@ndtuan412 ndtuan412 commented Nov 28, 2019

Cool, thanks

@zaqisilverano

This comment has been minimized.

Copy link

@zaqisilverano zaqisilverano commented Nov 29, 2019

It works! Thankssss

@turigeza

This comment has been minimized.

Copy link

@turigeza turigeza commented Dec 1, 2019

@maceto2016

This comment has been minimized.

Copy link

@maceto2016 maceto2016 commented Dec 7, 2019

Perfect! Thanks!

@EmilienLeroy

This comment has been minimized.

Copy link

@EmilienLeroy EmilienLeroy commented Dec 13, 2019

Thanks !

@Nikita-Polyakov

This comment has been minimized.

Copy link

@Nikita-Polyakov Nikita-Polyakov commented Dec 27, 2019

Thanks!

Here is the example in new syntax with customization:

<!-- pass through scoped slots -->
<template v-for="(_, scopedSlotName) in $scopedSlots" v-slot:[scopedSlotName]="slotData">
  <slot :name="scopedSlotName" v-bind="slotData" />
</template>

<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
  <slot :name="slotName" />
</template>

<!-- after iterating over slots and scopedSlots, you can customize them like this -->
<template v-slot:overrideExample>
    <slot name="overrideExample" />
    <span>This text content goes to overrideExample slot</span>
</template>
@Cactusx09

This comment has been minimized.

Copy link

@Cactusx09 Cactusx09 commented Feb 5, 2020

๐Ÿพ

@vsantin

This comment has been minimized.

Copy link

@vsantin vsantin commented Mar 5, 2020

Thanks! ๐Ÿ‘๐Ÿ‘๐Ÿ‘

@charsen

This comment has been minimized.

Copy link

@charsen charsen commented Apr 29, 2020

cool! Thanks! ๐Ÿ‘๐Ÿ‘๐Ÿ‘

@fontzter

This comment has been minimized.

Copy link

@fontzter fontzter commented May 23, 2020

๐Ÿ’ฏ just what i needed! thx

@jimbalatero

This comment has been minimized.

Copy link

@jimbalatero jimbalatero commented Jun 15, 2020

Thanks!

@tminich

This comment has been minimized.

Copy link

@tminich tminich commented Jun 24, 2020

Just going to point this out because I had some issues with Vuetify and passing through slots:
It is absolutely vital that you pass normal slots AFTER scoped slots, exactly like Nikita-Polyakov posted, not like the old format of OP.

Reason:
Vuetify ignores scoped slots if they are not supposed to be scoped. Normal slots are always added to both 'simple' slots and scoped slots, so their scoped version will overwrite the 'simple' version and essentially remove them from 'simple' slots when passed through.

Edit: I have to partially take back what I said, the final verdict right now is that, at least with Vuetify, the new syntax simply doesn't work at all. Either normal slots don't work or scoped slots (aka whatever you pass first) don't work.

@ctwowt

This comment has been minimized.

Copy link

@ctwowt ctwowt commented Jul 2, 2020

Thanks! you guys save my life...

@zaro

This comment has been minimized.

Copy link

@zaro zaro commented Jul 15, 2020

thanks, just what I needed

@DanRamage

This comment has been minimized.

Copy link

@DanRamage DanRamage commented Sep 16, 2020

""
I wish I'd seen this days ago, solved my issue!

@ffantasy

This comment has been minimized.

Copy link

@ffantasy ffantasy commented Sep 21, 2020

Thanks! you are awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You canโ€™t perform that action at this time.