LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1914 min read

Dynamic & Async Components

Switch between components dynamically with <component :is>, and load large components lazily.

The <component :is> Element

<component :is="..."> renders whichever component (or element) its is attribute currently points to, letting you switch which component is displayed based on state.

<script setup>
import { ref } from 'vue';
import HomeTab from './HomeTab.vue';
import ProfileTab from './ProfileTab.vue';
const currentTab = ref(HomeTab);
</script>
<template>
<component :is="currentTab" />
<button @click="currentTab = ProfileTab">Go to Profile</button>
</template>

A Tabbed Interface Example

A common use is switching by name, looked up from a map of tab keys to components.

<script setup>
import { ref, shallowRef } from 'vue';
import HomeTab from './HomeTab.vue';
import ProfileTab from './ProfileTab.vue';
const tabs = { home: HomeTab, profile: ProfileTab };
const activeTabKey = ref('home');
</script>
<template>
<button @click="activeTabKey = 'home'">Home</button>
<button @click="activeTabKey = 'profile'">Profile</button>
<component :is="tabs[activeTabKey]" />
</template>
Use shallowRef for Component References

When a ref holds a component definition (not plain data), shallowRef avoids Vue trying to deeply make the component object itself reactive, which is unnecessary overhead.

Async Components

defineAsyncComponent wraps a dynamic import() so a large or rarely-used component only loads when it's actually rendered — reducing your initial bundle size.

<script setup>
import { defineAsyncComponent } from 'vue';
const HeavyChart = defineAsyncComponent(() =>
import('./HeavyChart.vue')
);
</script>
<template>
<Suspense>
<HeavyChart />
<template #fallback>
<p>Loading chart…</p>
</template>
</Suspense>
</template>

FAQs

Yes — passing a string like "h1" or "section" renders that native element, which is handy for polymorphic components.

By default, no — the previous component fully unmounts. Wrap it in <KeepAlive> if you want to preserve state across switches.

Summary

<component :is> switches which component renders based on state, and defineAsyncComponent defers loading a component's code until it's actually needed. Next, you'll learn provide/inject for passing data through many layers of nesting at once.

Next Lesson →

Provide / Inject