You know how to pass data into a slot by using scoped slots, but how do you communicate back?

You pass a method into our slot and then call that method in the slot. You can't emit an event, because slots share the same context (or scope) as the parent component.

// Parent.vue
<template>
  <Child>
    <template #default="{ clicked }">
      <button @click="clicked">
        Click this button
      </button>
    </template>
  </Child>
</template>
// Child.vue
<template>
  <div>
    <!-- Pass `handleClick` as `clicked` into our slot -->
    <slot :clicked="handleClick" />
  </div>
</template>

In this article we'll cover why this works, as well as:

  • Emitting from slot to parent
  • What it means when a slot shares scope with the parent
  • Emitting from slot to grandparent component
  • A more in-depth look at how you can communicate back from a slot using methods

Emitting from a slot to the parent

Let's take a look at just the Parent component for now:

// Parent.vue
<template>
  <Child>
    <button @click="">
      Click this button
    </button>
  </Child>
</template>

We have a button inside of the slot of the Child component. When that button is clicked, we want to call a method inside of the Parent component.

If the button wasn't in the slot, but instead a child of the Parent component directly, we have access to the method on the component:

// Parent.vue
<template>
  <button @click="handleClick">
    Click this button
  </button>
</template>

The same is true for when that button component is inside of a slot:

// Parent.vue
<template>
  <Child>
    <button @click="handleClick">
      Click this button
    </button>
  </Child>
</template>

This works because the slot shares the same scope as the Parent component.

Slots and template scope

I've talked about scope in Vue components before, but this is a new type of scope that I haven't talked about.

(I'll call this one "template scope", and I'll need to do a follow-up to that article at some point!)

This is what template scope is: everything inside of a template has access to everything defined on a component.

This includes all elements, all slots, and all scoped slots.

So no matter where that button is located in the template, it has access to the handleClick method.

This may seem weird at first, and it's one of the reasons why slots are tricky to understand. The slot ends up being rendered as a child of our Child component, but it doesn't share scope with the Child component. Instead, it acts as if it's a child of the Parent component.

I explore this idea β€” that slots pretend to be something they're not β€”Β more in this article.

Emitting from a slot to the grandparent

If we want to emit from the slot to the grandparent component, we use the regular $emit method:

// Parent.vue
<template>
  <Child>
    <button @click="$emit('click')">
      Click this button
    </button>
  </Child>
</template>

Because the slot shares the same template scope as the Parent component, calling $emit here will emit an event from the Parent component.

Emitting from a slot back to the child

What about communicating back to the Child component?

We just saw that calling $emit in the slot will emit an event from the Parent to our Grandparent component, so that's ruled out.

But we know how to pass data into the slot from the child:

// Child.vue
<template>
  <div>
    <slot :data="data" />
  </div>
</template>

And how to use it within the scoped slot:

// Parent.vue
<template>
  <Child>
    <template #default="{ data }">
      {{ data }}
    </template>
  </Child>
</template>

Instead of just passing data, we can also pass methods into the scoped slot. If we hook up those methods in the right way, we can use that to communicate back to the Child component:

// Parent.vue
<template>
  <Child>
    <template #default="{ clicked }">
      <button @click="clicked">
        Click this button
      </button>
    </template>
  </Child>
</template>
// Child.vue
<template>
  <div>
    <!-- Pass `handleClick` as `clicked` into our slot -->
    <slot :clicked="handleClick" />
  </div>
</template>

Whenever the button is clicked, the handleClick method from the Child component is called.

If you're interested in learning more about these kinds of patterns, I'm working on a new course on Reusable Components that covers slots in extreme detail.