Vue.js: notes on Slots

1 min readvue

Vue slots allow us to create generic components, by passing template content (custom markup) to child components.

Let’s say we have a MyButton component which we’d like to make generic. We can set a slot inside its template:

<button>
<slot></slot>
</button>

We can then pass custom content to MyButton:

<MyButton>Submit</MyButton>

The HTML node Submit replaces the tag <slot></slot> when the component gets rendered.

Optionally, we can specify some fallback content, to be rendered when the parent does not provide content for a slot:

<button>
<slot>Click me</slot>
</button>

We’re not limited to text, we can also pass any valid template content:

<MyButton>
<strong>S</strong>ubmit
</MyButton>

Since we didn’t explicitly name the slot above, it will implicitly become the default slot, named default. The following achieves the same result as the above:

<MyButton>
<template v-slot:default>
<strong>S</strong>ubmit
</template>
</MyButton>

Instead of v-slot:, we can use the shorthand #, as in <template #default>.

Named slots start to become useful when we want our component to provide more than one slot:

<button>
<slot name="emoji"></slot>
<slot>Click me</slot>
</button>
<MyButton>
<template v-slot:emoji>👍</template>
<template v-slot:default>
<strong>S</strong>ubmit
</template>
</MyButton>

Sometimes we may have some state inside our child component that we’d need the parent to be aware of (to render the content of a slot). We can use scoped slots to achieve that:

<button @mouseenter="hover = true" @mouseleave="hover = false">
<slot name="emoji" :hover="hover"></slot> <slot>Submit</slot>
</button>
{% raw %}
<MyButton>
<template #emoji="{ hover }">{{ hover ? '👍' : '👎' }}</template>
</MyButton>
{% endraw %}