vue.jscomponents

In VueJS, Passing variable instead of string name of component to :is attribute of dynamic component does not work


In the code example below (referenced in VueJS Official Site) :

<script setup>
import Home from './Home.vue'
import Posts from './Posts.vue'
import Archive from './Archive.vue'
import { ref, watch } from 'vue'
 
let currentTab = ref('Home')

const tabs = {
  Home,
  Posts,
  Archive
}

watch(currentTab, (currentTab) => console.log(tabs[currentTab], currentTab))
</script>

<template>
  <div class="demo">
    <button
       v-for="(_, tab) in tabs"
       :key="tab"
       :class="['tab-button', { active: currentTab === tab }]"
       @click="currentTab = tab"
     >
      {{ tab }}
    </button>
      <component :is="tabs[currentTab]" class="tab"></component>
  </div>
</template>

<style>
.demo {
  font-family: sans-serif;
  border: 1px solid #eee;
  border-radius: 2px;
  padding: 20px 30px;
  margin-top: 1em;
  margin-bottom: 40px;
  user-select: none;
  overflow-x: auto;
}

.tab-button {
  padding: 6px 10px;
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
  border: 1px solid #ccc;
  cursor: pointer;
  background: #f0f0f0;
  margin-bottom: -1px;
  margin-right: -1px;
}
.tab-button:hover {
  background: #e0e0e0;
}
.tab-button.active {
  background: #e0e0e0;
}
.tab {
  border: 1px solid #ccc;
  padding: 10px;
}
</style>

taken from

Code Example

Code Example Reference on VueJS Official Page

... when i pass variable 'currentTab' to :is attribute (instead of tabs[currentTab], as below:

<component :is="currentTab" class="tab"></component>

... strangely the component stop showing, despite the fact value of variable 'currentTab' should resolve into the string value of a Component Name on which we click (like, 'Home', or 'Posts', or 'Archive' as you can see the value of variable that i display using console.log through "watch()" ).

So, the code works only when i pass "tabs[currentTab]" to :is attribute or when i pass the string name of the component (Home, or Posts, or Archive) as below:

<component :is="Archive" class="tab"></component> // shows only Archive OR <component :is="tabs[currentTab]" class="tab"></component> //Shows component w.r.t tab clicked.

If the variable 'currentTab' should resolve into its value to give:

<component :is="Archive" class="tab"></component> // shows only Archive

... then why there is strange behavior in resulting view ?


Solution

  • In case of :is="Archive" and :is="tabs[currentTab]" the value is a component, while in case of :is="currentTab" it's a string.

    It should either be used with a map of components like tabs, or resolved prior to usage:

    let currentTabComp = computed(() => resolveComponent(currentTab.value))
    

    This is not well-supported by script setup syntax, in order for this to work, the components need to be explicitly registered in component components option with regular script.