17

A simple working example of a Vue2 dynamic component


<template>
    <div>
        <h1>O_o</h1>
        <component :is="name"/>
        <button @click="onClick">Click me !</button>
    </div>
</template>

<script>
    export default {
        data: () => ({
            isShow: false
        }),
        computed: {
            name() {
                return this.isShow ? () => import('./DynamicComponent') : '';
            }
        },
        methods: {
            onClick() {
                this.isShow = true;
            }
        },
    }
</script>

Everything works, everything is great. I started trying how it would work with the Composition API.

<template>
    <div>
        <h1>O_o</h1>
        <component :is="state.name"/>
        <button @click="onClick">Click me !</button>
    </div>
</template>

<script>
    import {ref, reactive, computed} from 'vue'

    export default {
        setup() {
            const state = reactive({
                name: computed(() => isShow ? import('./DynamicComponent.vue') : '')
            });

            const isShow = ref(false);

            const onClick = () => {
                isShow.value = true;
            }

            return {
                state,
                onClick
            }
        }
    }
</script>

We launch, the component does not appear on the screen, although no errors are displayed.

5 Answers 5

25

You can learn more about 'defineAsyncComponent' here https://labs.thisdot.co/blog/async-components-in-vue-3

or on the official website https://v3.vuejs.org/api/global-api.html#defineasynccomponent

import { defineAsyncComponent, defineComponent, ref, computed } from "vue"

export default defineComponent({
setup(){
const isShow = ref(false);
const name = computed (() => isShow.value ? defineAsyncComponent(() => import("./DynamicComponent.vue")): '')

const onClick = () => {
    isShow.value = true;
  }
 }
})
Sign up to request clarification or add additional context in comments.

Comments

15

Here is how you can load dynamic components in Vue 3. Example of dynamic imports from the icons collection inside /icons folder prefixed with "icon-".

BaseIcon.vue

<script>
import { defineComponent, shallowRef } from 'vue'

export default defineComponent({
  props: {
    name: {
      type: String,
      required: true
    }
  },
  setup(props) {
    // use shallowRef to remove unnecessary optimizations
    const currentIcon = shallowRef('')

    import(`../icons/icon-${props.name}.vue`).then(val => {
      // val is a Module has default
      currentIcon.value = val.default
    })

    return {
      currentIcon
    }
  }
})
</script>

<template>
    <svg v-if="currentIcon" width="100%" viewBox="0 0 24 24" :aria-labelledby="name">
      <component :is="currentIcon" />
    </svg>
</template>

You don't need to use computed or watch. But before it loads and resolved there is nothing to render, this is why v-if used.

UPD So if you need to change components (icons in my case) by changing props use watchEffect as a wrapper around the import function.

watchEffect(() => {
  import(`../icons/icon-${props.name}.vue`).then(val => {
    currentIcon.value = val.default
  })
})

Don't forget to import it from vue =)

Comments

4

Instead of string you should provide Component

<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>

<template>
  <component :is="Foo" />
  <component :is="someCondition ? Foo : Bar" />
</template>

1 Comment

This is an important detail. Thanks!
4

To solve the same problem, I used <script> in addition to <script setup>, and imported the component in it

<template>
    <component :is="step">
               
   </component>
</template>

<script setup>
import { ref, computed, reactive } from 'vue';
const step = ref('what-budget');
</script>

<script>
import WhatBudget from "../components/Quiz/WhatBudget.vue";
export default {
   components: {
      WhatBudget
   },
}
</script>

1 Comment

There's nothing dynamic about this. — Your issue should actually be fixed by 1) removing your second script tag 2) adding import WhatBudget from "../components/Quiz/WhatBudget.vue"; before your const step 3) change ref('what-budget') to ref(WhatBudget)...
-1

The component should be added to components option then just return it name using the computed property based on the ref property isShow :

components:{
   MyComponent:defineAsyncComponent(() => import("./DynamicComponent.vue"))
},
setup(){
const isShow = ref(false);
const name = computed (() => isShow.value ? 'MyComponent': '')

const onClick = () => {
    isShow.value = true;
}
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.