First, I'll show you how, then we'll get into why you'd want to hide slots.
Every Vue component has a special $slots
object with all of your slots in it. The default slot has the key default
, and any named slots use their name as the key:
const $slots = {default: <default slot>,icon: <icon slot>,button: <button slot>,};
But this $slots
object only has the slots that are applied to the component, not every slot that is defined.
Take this component that defines several slots, including a couple named ones:
<!-- Slots.vue --><template><div><h2>Here are some slots</h2><slot /><slot name="second" /><slot name="third" /></div></template>
If we only apply one slot to the component, only that slot will show up in our $slots
object:
<template><Slots><template #second>This will be applied to the second slot.</template></Slots></template>
$slots = { second: <vnode> }
We can use this in our components to detect which slots have been applied to the component, for example, by hiding the wrapper element for the slot:
<template><div><h2>A wrapped slot</h2><div v-if="$slots.default" class="styles"><slot /></div></div></template>
Now the wrapper div
that applies the styling will only be rendered if we actually fill that slot with something.
If we don't use the v-if
, we will have an empty and unnecessary div
if we don't have a slot. Depending on what styling that div
has, this could mess up our layout and make things look weird.
There are three main reasons to use a conditional slot:
For example, when we're adding default styles, we're adding a div
around a slot:
<template><div><h2>This is a pretty great component, amirite?</h2><div class="default-styling"><slot ></div><button @click="$emit('click')">Click me!</button></div></template>
However, if no content is applied to that slot by the parent component, we'll end up with an empty div
rendered to the page:
<div><h2>This is a pretty great component, amirite?</h2><div class="default-styling"><!-- No content in the slot, but this divis still rendered. Oops. --></div><button @click="$emit('click')">Click me!</button></div>
Adding that v-if
on the wrapping div
solves the problem though. No content applied to the slot? No problem:
<div><h2>This is a pretty great component, amirite?</h2><button @click="$emit('click')">Click me!</button></div>
Here's a Codesandbox with a working demo if you want to take a look: https://codesandbox.io/s/reactive-slots-bth28?file=/src/components/HasSlot.vue
I wrote more tips on slots in this article: Tips to Supercharge Your Slots (Named, Scoped, and Dynamic)