Skip to content

Instantly share code, notes, and snippets.

@Flexicon
Forked from loilo/pass-slots.md
Created April 28, 2020 11:23
Show Gist options
  • Save Flexicon/e0e5de300125c54ba5e16ff0060e505a to your computer and use it in GitHub Desktop.
Save Flexicon/e0e5de300125c54ba5e16ff0060e505a to your computer and use it in GitHub Desktop.
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment