
How dynamically lazy load vue 3 components for better performance
Let's see how we can dynamically lazy load vue 3 components by using defineAsyncComponent.
Large Components Issue
When you application grows, developers face a new challenge, loading large components.
And it's hard to address that without compromising application performance and it might lead to Massive initial bundle sizes and definitely Poor user experience.
but, in this article we want to know how we can properly handle this in Vue 3
applications🌵.
Scenario
Imagine you have number of big components that you want load them dynamically based on the input or prop,
you might say, Okay I just Import them and use v-if
to render them conditionally!
<template>
<BigComponent v-if="size === 'big'" />
<BiggerComponent v-if="size === 'bigger'" />
<BiggestComponent v-if="size === 'biggest'" />
</template>
But the problem is that you just render them conditionally and all of them will be bundled and imported at build time! so the problem is still exist.
The Magic of defineAsyncComponent
the defineAsyncComponent
helps us to load components when we need them.
So we can re-write the example like this
<script setup>
const BigComponent = defineAsyncComponent(() => import('./BigComponent.vue')) const BiggerComponent = defineAsyncComponent(() => import('./BiggerComponent.vue')) const BiggestComponent = defineAsyncComponent(() => import('./BiggestComponent.vue')) </script>
<template>
<BigComponent v-if="size === 'big'" />
<BiggerComponent v-if="size === 'bigger'" />
<BiggestComponent v-if="size === 'biggest'" />
</template>
Now components will be simply rendered only if needed.
For advanced usage we can pass an object to
defineAsyncComponent
to handle loading, error, timeout and etc.
Real-World Example
We need a components that decide to show an Icon (Represents any heavy component or parts of application) based on the prop value.
<script setup lang="ts">
import { defineAsyncComponent, computed } from 'vue'
const definedProps = defineProps<{ extension: string }>()
const iconComponents = {
json: () => import('./Icons/JSON.vue'),
html: () => import('./Icons/HTML.vue'),
shell: () => import('./Icons/Shell.vue'),
js: () => import('./Icons/JavaScript.vue'),
ts: () => import('./Icons/TypeScript.vue'),
}
const Icon = computed(() => defineAsyncComponent(iconComponents[definedProps.extension]))
</script>
<template>
<Suspense>
<template #default>
<component :is="Icon" />
</template>
<template #fallback>
<span>icon</span>
</template>
</Suspense>
</template>
In here we combined
defineAsyncComponent
andSuspense
to handle loading state of async component.
This approach ensures that you only load and render what we want!
A couple of Question and Answer about this topic
defineAsyncComponent()
enables asynchronously loading of Vue components, allowing you to dynamically import components only when they are needed. This helps reduce initial bundle size, improve application performance, and load components on-demand, preventing unnecessary upfront resource allocation.
v-if
simply shows or hides components in the DOM but doesn't prevent initial bundling. Lazy loading with defineAsyncComponent()
ensures that components are not even imported until they are required, significantly reducing the initial JavaScript payload and improving application load times.
Asynchronous component loading allows applications to:
- Reduce initial bundle size
- Improve initial page load speed
- Load components only when necessary
- Minimize memory consumption
- Provide a more responsive user experience, especially on mobile devices or slower networks
<component :is="..." />
provides dynamic component rendering, but doesn't inherently implement lazy loading. To achieve lazy loading, you must combine it with defineAsyncComponent()
and dynamic imports, which enables on-demand component loading based on runtime conditions.
defineAsyncComponent()
supports error handling through optional configuration:
const AsyncComponent = defineAsyncComponent({
loader: () => import('./MyComponent.vue'),
errorComponent: ErrorHandler, // Component shown on load failure
delay: 200, // Delay before showing loading state
timeout: 3000 // Timeout for component loading
})