The New Default. Your hub for building smart, fast, and sustainable AI software

See now
Vue.js development services - dynamic components in Vue 3

Vue 3 Dynamic Components Explained: Comprehensive Guide by Vue.js Expert

Daniel Kasperczyk
|   Jul 19, 2024

This article was originally written by Daniel Kasperczyk in July 2024. We reviewed it in October 2026 to confirm the examples still reflect current Vue 3 (the stable 3.5.x line), fixed a few code details and added a summary, key takeaways and FAQ. The author's insights are unchanged.

Dynamic components in Vue 3 let you render different components in the same place in a template by binding the built-in <component> element's is attribute to whichever component should be shown. The Vue.js framework, a top choice for any company seeking simplicity and flexibility, offers numerous ways to render components dynamically, and dynamic components are one of the most useful. They improve the interactivity and modularity of web applications and let teams switch between elements within a single template, keeping the codebase streamlined and maintainable.

Executive Summary

Vue 3 dynamic components (<component :is="...">) replace chains of v-if/v-else-if branches with a single element that renders whichever component the current state points to. That keeps templates readable and props or event handlers declared once, which makes UI logic cheaper to maintain as an application grows.

Combined with <KeepAlive> for preserving state and defineAsyncComponent for lazy loading, they cover common patterns like tabs, multi-step forms and dashboards while keeping the initial bundle small.

What Are Dynamic Components in Vue 3?

Experienced Vue developers rely on simple and intuitive syntax that keeps the template tags clear: they specify which component should be displayed and under what circumstances. Dynamic components help create responsive and adaptive user interfaces that adjust to the application's specific needs and contexts. We particularly value this functionality in enterprise-scale applications.

Let's get into the practical applications and benefits of using dynamic components in Vue 3. I will showcase how they can simplify complex UI logic and improve overall development efficiency.

Dynamic components in Vue 3 offer a solution for changing components during user interaction while maintaining code readability within the presentation layer (template tags). This desirable effect makes it easier for others to understand the code we write and, most importantly, maintain it in the future.

With dynamic Vue components, we don't need to deal with multiple conditional structures such as v-if, v-else, and v-else-if. Instead of replicating the same props or emitted actions across numerous conditional branches, dynamic components enable a cleaner, more efficient approach. It results in more concise and transparent code, allowing for smoother development and easier maintenance.

To render a dynamic component in Vue, use straightforward syntax consisting of the built-in <component> element and pass it an attribute that indicates which element should be rendered. The key attribute used here is is. According to the Vue documentation, it accepts a registered component's name, an HTML tag name, or the component definition itself. In <script setup>, you pass the imported component (the definition), not a string. For example, you can bind :is="currentComponent", where currentComponent is a reactive value that determines which component to display at any given time.

<component :is="currentComponent" />

How Do You Pass Props and Events to Dynamic Components in Vue 3?

If we want to pass props or events to a dynamic component, we can do so just as with standard Vue components:

<component :is="currentComponent" :value="value" @input="onInput" />
<!-- or -->
<component :is="currentComponent" v-model="value" />

The two forms aren't interchangeable. On <component>, v-model expands to a modelValue prop and an update:modelValue event, so it works with your own components but won't work when is resolves to a native element like input, select or textarea. For native elements, use the explicit :value and @input pair from the first line.

How Do You Keep Component State When Switching Dynamic Components?

When a dynamic component changes, the previous one is unmounted, meaning all data it held before the change will be lost. This behavior may not always be what we expect, especially in scenarios where maintaining the component's state is crucial, such as in form inputs, user sessions, or any element with a complex internal state that should persist across different views.

To address this issue, the Vue team has prepared a built-in component called <keep-alive> (<KeepAlive>) for such situations. It wraps dynamic components and caches their instances instead of destroying them, thereby preserving their state. When a component wrapped in <keep-alive> is no longer active, it's removed from the DOM but kept in a deactivated state rather than unmounted, so it comes back exactly as the user left it.

<keep-alive>
  <component :is="currentComponent" />
</keep-alive>

By default, every component inside is cached. If you switch between many heavy views, use the include/exclude props to limit which components are cached and max to cap the number of cached instances.

How Do You Lazy Load Dynamic Components in Vue 3?

Lazy loading is a technique for improving the performance of a Vue 3 application by loading dynamic components only when they are needed. This approach is particularly beneficial in large applications, where loading all Vue components upfront can result in longer initial load times and increased resource usage. By lazy loading dynamic components, you can ensure that only the necessary parts of your application are loaded, resulting in faster load times and a smoother user experience.

In Vue 3, lazy loading components is straightforward with the defineAsyncComponent function. This function enables you to define components that will be loaded asynchronously only when needed, optimizing your application's performance and reducing initial load times.

<template>
  <button @click="componentToRender = 'welcome'">Welcome component</button>
  <button @click="componentToRender = 'hello'">Hello component</button>
  <component :is="currentComponent" />
</template>

<script setup lang="ts">
import { defineAsyncComponent, computed, ref } from 'vue'

const asyncComponents = {
  welcome: defineAsyncComponent(() => import('@/components/TheWelcome.vue')),
  hello: defineAsyncComponent(() => import('@/components/HelloWorld.vue'))
}

const componentToRender = ref<keyof typeof asyncComponents>('hello')

const currentComponent = computed(() => {
  return asyncComponents[componentToRender.value]
})
</script>

Real-Life Example: How Do You Build a Multi-Step Form with Vue 3 Dynamic Components?

Dynamic components can be used in various scenarios, such as multi-step forms commonly found on e-commerce platforms. In these forms, users must provide their information, choose delivery options, and select payment methods.

In this example, we won't use the built-in <keep-alive> component because we'll pass data to the dynamic component using attributes, but we will use lazy loading to improve performance.

<template>
  <div>
    <component :is="currentStep"
               :data="formData"
               @next="nextStep"
               @prev="prevStep"
    />

    <div class="buttons">
      <button v-if="currentStepIndex !== 0" @click="prevStep">Previous</button>
      <button v-if="currentStepIndex !== steps.length - 1" @click="nextStep()">Next</button>
      <button v-else @click="submitForm">Submit</button>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, defineAsyncComponent } from 'vue';

const steps = [
  defineAsyncComponent(() => import('./Step1.vue')),
  defineAsyncComponent(() => import('./Step2.vue')),
  defineAsyncComponent(() => import('./Step3.vue'))
];

const currentStepIndex = ref(0);
const formData = ref({});

const currentStep = computed(() => steps[currentStepIndex.value]);

const nextStep = (data) => {
  formData.value = { ...formData.value, ...data };
  currentStepIndex.value++;
};

const prevStep = () => {
  currentStepIndex.value--;
};

const submitForm = () => { /* ... */ };
</script>

The step components are defined once, outside computed. Creating them inside it would produce a new component definition on every step change, which forces Vue to treat each step as a brand-new component.

Key Takeaways

  • <component :is> replaces v-if/v-else-if chains with one element, so props and events are declared once.

  • In <script setup>, bind is to the imported component, not a string name.

  • v-model on <component> targets modelValue, so it won't work with native inputs. Use :value and @input instead.

  • Wrap dynamic components in <keep-alive> to preserve state, and cap the cache with max when switching between many views.

  • Combine defineAsyncComponent with dynamic components to load each view only when it's needed.

When Should You Use Dynamic Components in Vue 3?

Understanding and effectively using dynamic components in Vue 3 can significantly improve both your development process and your application's user experience. By offering a streamlined way to switch between Vue components and manage state, dynamic components allow for more maintainable, readable, and efficient code. The built-in <keep-alive> component further extends this functionality by preserving component state across changes, ensuring a smooth and consistent user experience. Whether you're building multi-step forms, dynamic dashboards, or interactive user interfaces, dynamic components are a powerful toolset for creating responsive and adaptive applications that stay modular as they grow.

If you're planning a Vue application and want a team that has shipped patterns like these in production, take a look at Monterail's Vue.js development services and get in touch.

Vue 3 Dynamic Components FAQ

Daniel Kasperczyk
Daniel Kasperczyk
JavaScript Developer at Monterail
Linkedin
Daniel Kasperczyk is a front-end developer at Monterail, specializing in Vue.js and React. He's passionate about writing clean, maintainable code, backend development, and expanding full-stack capabilities. When off the clock, Daniel explores financial markets, trading stocks, and forex.