Hide an Empty Slot with No Content

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 would end up with an empty and unnecessary div if we didn't have a slot. Depending on what styling that div has, this could mess up our layout and make things look weird.

So why do we want to be able to conditionally render slots?

There are three main reasons to use a conditional slot:

  1. When using wrapper divs to add default styles
  2. The slot is empty
  3. If we're combining default content with nested slots

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 div
is 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>