Determining if slot content is null or empty

@kano's answer works well, but there's a gotcha: this.$slots isn't reactive, so if it starts out being false, and then becomes true, any computed property won't update.

The solution is to not rely on a computed value but instead on created and beforeUpdated (as @MathewSonke points out):

export default {
  name: "YourComponentWithDynamicSlot",
  data() {
    return {
      showFooter: false,
      showHeader: false,
    };
  },
  created() {
    this.setShowSlots();
  },
  beforeUpdate() {
    this.setShowSlots();
  },
  methods: {
    setShowSlots() {
      this.showFooter = this.$slots.footer?.[0];
      this.showHeader = this.$slots.header?.[0];
    },
  },
};

UPDATE: Vue 3 (Composition API)

For Vue 3, it seems that the way to check whether a slot has content has changed (using the new composition API):

import { computed, defineComponent } from "vue";

export default defineComponent({
  setup(_, { slots }) {
    const showHeader = computed(() => !!slots.header);

    return {
      showHeader,
    };
  },
});

note: I can't find any documentation on this, so take it with a pinch of salt, but seems to work in my very limited testing.


You'd need a computed property which checks for this.$slots. With a default slot you'd check this.$slots.default, and with a named slot just replace default with the slot name.

computed: {
  slotPassed() {
    return !!this.$slots.default[0].text.length
  }
}

And then use it in your template:

<template>
  <div>
    <slot v-if="slotPassed">Loading...</slot>
    <p v-else>Searching...</p>
  </div>
</template>

You can see a small example here. Notice how fallback content is displayed and not "default content", which is inside the slot.


Edit: My wording could've been better. What you need to do is check for $slots.X value, but computed property is a way to check that. You could also just write the slot check in your template:

<template>
  <div>
    <slot v-if="!!$slots.default[0].text">Loading...</slot>
    <p v-else>Searching...</p>
  </div>
</template>

Edit 2: As pointed out by @GoogleMac in the comments, checking for a slot's text property fails for renderless components (e.g. <transition>, <keep-alive>, ...), so the check they suggested is:

!!this.$slots.default && !!this.$slots.default[0]

// or..
!!(this.$slots.default || [])[0]

Tags:

Vue.Js