Vue async components let you split large parts of your application into separate JavaScript chunks that load only when needed. Using defineAsyncComponent() with a dynamic import(), Vite bundles heavy widgets separately so your initial page loads faster.
This approach works best for components users do not see right away, such as rich text editors, interactive charts, PDF viewers, or admin settings modals.
flowchart LR
Main["Initial App Bundle"] --> Action["User Clicks Show Chart"]
Action --> Fetch["Lazy Fetch HeavyChart.js Chunk"]
Fetch --> Render["Render Chart Component"]
defineAsyncComponent Optionsloader: A factory function returning a dynamic import promise: () => import('./HeavyChart.vue').loadingComponent: A lightweight spinner or skeleton displayed while the chunk downloads.errorComponent: A fallback UI rendered if the network request fails or times out.delay: Milliseconds to wait before showing loadingComponent (default 200ms) to prevent quick flashes.timeout: Maximum milliseconds before showing errorComponent.Here, HeavyChart is not downloaded when the page opens. Clicking the button sets showChart to true, which triggers the lazy network import inside <script setup>.
<script setup>
import { ref, defineAsyncComponent } from 'vue';
import LoadingSpinner from './components/LoadingSpinner.vue';
import ErrorNotice from './components/ErrorNotice.vue';
const HeavyChart = defineAsyncComponent({
loader: () => import('./components/HeavyChart.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorNotice,
delay: 200,
timeout: 5000
});
const showChart = ref(false);
</script>
<template>
<section class="analytics-page">
<h2>Performance Overview</h2>
<button @click="showChart = !showChart">
{{ showChart ? 'Hide Chart' : 'Load Analytics Chart' }}
</button>
<HeavyChart v-if="showChart" />
</section>
</template>
defineAsyncComponent(import('./Comp.vue')) starts loading immediately on startup. Always wrap it in an arrow function: () => import('./Comp.vue').defineAsyncComponent() adds extra HTTP requests that hurt speed. Reserve async loading for large modules or conditional modals.defineAsyncComponent for Route Pages: Vue Router already supports lazy routes natively via component: () => import('./views/AboutView.vue'). Do not wrap route components inside defineAsyncComponent.defineAsyncComponent() during production builds?Vite detects the dynamic import() inside loader and automatically creates a separate JavaScript chunk file that browsers fetch only when the component renders.
defineAsyncComponent together with <Suspense>?Yes. Async components work with <Suspense> automatically by default. If you want an async component to manage its own loadingComponent instead, set suspensible: false in its options.
Yes. Pass the result of defineAsyncComponent(() => import('./MyModal.vue')) to app.component('MyModal', AsyncModal) in main.js.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With