'Vue js Vuetify custom component not rendered based on the display breakpoint

I have a custom component that basically is a v-btn component with specific style. When I'm using it inside v-menu activator with conditional based on display breakpoint, the custom component does not display on the screen. But if I use regular v-btn the button displays properly based on the display breakpoint. What am I doing wrong here?

https://codepen.io/jgunawan-dc/pen/XWzJqRy?editors=1010

<div id="app">
  <v-app id="inspire">
    <div class="text-center">
      <v-menu offset-y>
        <template v-slot:activator="{ on, attrs }">
          <global-custom-button
            v-if="$vuetify.breakpoint.mdAndDown"
            v-bind="attrs"
            v-on="on"
          >
            Show on medium and lower
          </global-custom-button>
          <v-btn v-else
            color="primary"
            dark
            v-bind="attrs"
            v-on="on"
          >
            Dropdown
          </v-btn>
        </template>
        <v-list>
          <v-list-item
            v-for="(item, index) in items"
            :key="index"
          >
            <v-list-item-title>{{ item.title }}</v-list-item-title>
          </v-list-item>
        </v-list>
      </v-menu>
    </div>
  </v-app>
</div>
Vue.component('global-custom-button', {
  template: '<v-btn outlined color="info" @click="$emit(\'click\', $event)"><slot></slot></v-btn>'
});
new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: () => ({
    items: [
      { title: 'Click Me' },
      { title: 'Click Me' },
      { title: 'Click Me' },
      { title: 'Click Me 2' },
    ],
  }),
})


Solution 1:[1]

For a quick fix, use v-show as @Rotiken said.

As another solution you could also register two global components and show/hide it using extra condition prop:

<v-menu offset-y>
  <template v-slot:activator="{ on, attrs }">
    <global-custom-button
      :condition="$vuetify.breakpoint.mdAndDown"
      v-bind="attrs"
      v-on="on"      
    >
      Show on medium and lower
    </global-custom-button>
          
    <global-custom-button-2
      :condition="!$vuetify.breakpoint.mdAndDown"
      v-bind="attrs"
      v-on="on"
    >
      Dropdown
    </global-custom-button-2>
  </template>
  ...
</v-menu>

...

Vue.component('global-custom-button', {
  template: '<v-btn v-if="condition" outlined color="info" @click="$emit(\'click\', $event)"><slot></slot></v-btn>',
  props: ['condition']
});

Vue.component('global-custom-button-2', {
  template: '<v-btn v-if="condition" color="primary" dark @click="$emit(\'click\', $event)"><slot></slot></v-btn>',
  props: ['condition']
});
...

According to this CodePen, this also works.


Why this happens and why v-show works fine with same template: I can't give an exact answer, but there are some assumptions.

Vue.js docs says that v-if removes objects from DOM, whereas v-show changes its display state in CSS, but keep it in DOM.

Since you are using v-if in a vuetify v-menu component, perhaps the component has some methods for updating the v-slot:activator content that can conflict (and not performed at the same time) with conditional rendering using v-if and vuetify display breakpoints.

If you are familiar with TypeScript, you can look into v-menu sources or into activatable mixin sources. Maybe here you will find the true reason for this behavior.

If you just want to avoid such problems, use v-show in such cases.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alexander Shkirkov