Learn everything you need to know about using slots in Vue.js to create customizable, reusable components for your apps.
When you are building reusable components with Vue.js, you may need to inject some kind of content from the parent. This could be things like including some HTML tags or even components in some specific parts.
If you have no idea how to proceed, the good news is that after reading this article, you will understand most of the things you should know about a handy Vue.js feature: slots.
When I started with Vue.js, I was not using slots very much. But to be honest with you, now, it’s one of my favorite features. It makes things so much easier when building popups or container boxes. I even surprised myself using them for more creative things like dedicating a left and right slot next to each option of my custom select component. This way, I can inject whatever I want, like a smiley, an image or some additional text.
Notes: In the most recent Vue minor release (2.6), Evan You announced some major updates to the feature. It now comes with a new syntax and performance improvements. You can read more about this here. We will adopt this new syntax throughout this article.
Let’s start with the default slot. When you do not specify any name in your slot, Vue will do it for you and call it “default.”
In the example below, we also defined some fallback content. This will appear when we didn’t define any content in the parent component. You can leave the <slot />
tag empty if you don’t need the fallback to be displayed.
Parent component
<template>
<MyChildComponent>
<h1>I am injecting some content in the default slot</h1>
</MyChildComponent>
</template>
Child component
<template>
<div class="my-child-component">
<slot>
<h1>
When no slot is specified in the parent, this fallback text will appear.
</h1>
</slot>
</div>
</template>
So, for a component with a basic layout, this works just fine. But life is complicated, and so are our components… 🙂
Introducing named slots, ladies and gentlemen! 🎩
Parent component
<template>
<ArticleTemplate>
<template #header>
<h1>Page Header</h1>
</template>
<h3>Article Title</h2>
<p>Article Content</p>
<template #footer>
<p>Page Footer</p>
</template>
</ArticleTemplate>
</template>
Child component
<template>
<header>
<slot name="header" />
</header>
<main>
<img src="logo.png" />
<slot />
<AuthorComponent />
</main>
<footer>
<slot name="footer" />
</footer>
</template>
As you can guess, named slots allow you to inject bits of content in different parts of the child component (directly from the parent component).
Note: The
#name
came to replace the oldv-slot="name"
syntax.
We did not attribute a name to the second slot (the one between the image and AuthorComponent
. Thus, it is considered to be the default slot. This is also why we did not need to wrap it in a template tag.
However, we still have a little issue. What if the parent component needs to access data or methods contained in the child component?
Let’s say for example that you have a list of fruits (with all the logic handling it) and you want to render them differently depending on the page you’re in.
Easy peasy lemon squeezy! 🍋
Child component
<template>
<ul>
<li v-for="fruit in fruits" :key="fruit.id">
<slot :fruit="fruit" :slice="slice" />
</li>
</ul>
</template>
<script>
export default {
props: {
fruits: {
type: Array,
required: true
}
},
methods: {
slice(id) {
// ... handles the fruit slicing Ninja style!
}
}
}
</script>
Now, using the attribute #fruit="{ fruit, slice }"
we achieve two things: the parent component tells the child component which slot it needs AND calls for the props it will use.
Parent component
<template>
<FruitList :fruits="fruitsOfTheNinja">
<template #default="{ fruit, slice }">
{{ fruit.name }}
<button @click="slice(fruit.id)">
⚔️ Style Slicing
</button>
</template>
</FruitList>
</template>
<script>
export default {
data() {
return {
fruitsOfTheNinja: [
{
id: 1,
name: '🍋Lemon'
},
{
id: 2,
name: '🥝Kiwi'
},
{
id: 3,
name: '🍑Peach'
},
{
id: 4,
name: '🍌Banana'
},
{
id: 5,
name: '🍎Apple'
}
]
}
}
}
</script>
And just like that our parent component can implement its own layout or use methods from the child component. 😎
But, what if you need to dynamically name your slots, and why would you want to do that? Well… There may be a time when each element in the array you’re feeding to the child component is different.
Let’s say our Chef 👩🍳 is creating recipes for the fruits array we used earlier. But he didn’t get enough time to create his recipe for each fruit listed.
So how do we only show the recipes for only the ones that have been made?
Child component
<template>
<ul>
<li v-for="fruit in fruits" :key="fruit.id">
<slot name="fruit" :fruit="fruit" :slice="slice" />
<slot name="recipe" :fruit="fruit" />
</li>
</ul>
</template>
<script>
export default {
props: {
fruits: {
type: Array,
required: true
}
},
methods: {
slice(id) {
// ... handles the fruit slicing Ninja style!
}
}
}
</script>
Parent component
<template>
<FruitList :fruits="fruitsOfTheNinja">
// Default List
<template #fruit="{ fruit, slice }">
{{ fruit.name }}
<button @click="slice(fruit.id)">
⚔️ Style Slicing
</button>
</template>
// Adding the Chef Recipes 👩🍳
<template v-if="fruit.recipe" #recipe="{ fruit }">
{{ fruit.recipe }}
<video>
<source src="video_pour_les_fins_gourmets.mp4" />
</video>
</template>
</FruitList>
</template>
<script>
export default {
data() {
return {
fruitsOfTheNinja: [
{
id: 1,
name: '🍋Lemon'
},
{
id: 2,
name: '🥝Kiwi',
recipe: 'Kiwi, Baijiu & Red Date Yoghurt Cocktail 🍸'
},
{
id: 3,
name: '🍑Peach'
},
{
id: 4,
name: '🍌Banana'
},
{
id: 5,
name: '🍎Apple'
}
]
}
}
}
</script>
We covered most of the things you should know when using slots. This really is one of the handiest features of Vue.js to create highly customizable (and thus reusable) components. Feel free to use them extensively in every one of your projects. You will never regret it! 😃
At the same time, if you have any question, feel free to reach me in the comments below or to ping me on Twitter @RifkiNada. 💁♀️
Want to learn more about creating great web apps? It all starts out with Kendo UI - the complete UI component library that allows you to quickly build high-quality, responsive apps. It includes everything you need, from grids and charts to dropdowns and gauges.
Nada is a JavaScript developer who likes to play with UI components to create interfaces with great UX. She specializes in Vue/Nuxt, and loves sharing anything and everything that could help her fellow frontend web developers. Nada also dabbles in digital marketing, dance and Chinese. You can reach her on Twitter @RifkiNada or visit her website, nadarifki.com.