Vue Slots
By Flavio Copes
Learn how Vue 3 slots let a parent inject content into a child, from the default slot to named slots and scoped slots with v-slot and the # shorthand.
A component can fully own its output:
<template>
<p>Hi {{ name }}</p>
</template>
<script setup>
defineProps(['name'])
</script>
Or it can let the parent component inject any kind of content into it, using slots.
What is a slot? It’s a space in your component output that is reserved, waiting to be filled. You reserve it with <slot></slot> in the child template:
<template>
<div class="user-information">
<slot></slot>
</div>
</template>
Anything between the child’s tags in the parent goes into that slot:
<UserInformation>
<h2>Hi!</h2>
<UserName name="Flavio" />
</UserInformation>
Content inside <slot>…</slot> in the child is the default, used when the parent passes nothing.
Named slots
A more complex component layout might need more than one slot. In that case you give each slot a name.
I use a Page.vue single file component in this example:
<template>
<div>
<main>
<slot></slot>
</main>
<aside>
<slot name="sidebar"></slot>
</aside>
</div>
</template>
The unnamed <slot> is the default slot (name="default").
In the parent, target a named slot with v-slot: (or the # shorthand) on a <template>:
<Page>
<template v-slot:sidebar>
<ul>
<li>Home</li>
<li>Contact</li>
</ul>
</template>
<h2>Page title</h2>
<p>Page content</p>
</Page>
Same thing with #:
<Page>
<template #sidebar>
<ul>
<li>Home</li>
<li>Contact</li>
</ul>
</template>
<h2>Page title</h2>
<p>Page content</p>
</Page>
Content that is not wrapped in a named v-slot goes into the default slot.
Older Vue 2 code used a
slot="sidebar"attribute on any tag. That API is gone. In Vue 3 usev-slot/#on a<template>.
Scoped slots
In a slot, the parent cannot access the data contained in the child component. Vue recognizes this use case and gives us scoped slots.
The child binds the data it wants to expose onto the <slot> tag:
<template>
<div>
<main>
<slot :dogName="dogName"></slot>
</main>
</div>
</template>
<script setup>
import { ref } from 'vue'
const dogName = ref('Roger')
</script>
(v-bind:dogName and :dogName are the same.)
In the parent we can access the dog name using the variable we assign to v-slot:
<Page>
<template v-slot="slotProps">
{{ slotProps.dogName }}
</template>
</Page>
slotProps is just a variable we used to access the props we passed. You can also avoid it and destructure the object on the fly:
<Page>
<template v-slot="{ dogName }">
{{ dogName }}
</template>
</Page>
For a named scoped slot:
<template #sidebar="{ dogName }">
{{ dogName }}
</template>
Slots are what you use when the parent decides the markup. When the parent only needs to hand over a value, a prop is the right tool.
Want me to talk about your product? You can sponsor this site.