0Pricing
Frontend Academy · Lesson

Async Components and Suspense

Load components lazily with defineAsyncComponent, wrap them in , and show fallback content while the component bundle loads.

Async Components and Suspense is a free Frontend Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Async Components?

Loading large or rarely-used components on demand (e.g. an admin panel, a chart library) keeps the initial bundle small. Vue's defineAsyncComponent handles the dynamic import lifecycle.

defineAsyncComponent

Pass a loader function that returns a promise resolving to a component. Vue handles loading, errors, and timeouts.

import { defineAsyncComponent } from 'vue';

const Dashboard = defineAsyncComponent(
  () => import('./Dashboard.vue')
);

// Use it like a normal component:
<template>
  <Dashboard />
</template>

Configuring Async Behaviour

Pass an options object to customise loading UI, error UI, delay before loading, and timeout.

const Dashboard = defineAsyncComponent({
  loader: () => import('./Dashboard.vue'),
  loadingComponent: LoadingSpinner,
  errorComponent: ErrorPanel,
  delay: 200,    // ms before showing loading
  timeout: 5000  // ms before showing error
});

Bundler Chunk Splitting

The dynamic import becomes a separate chunk in the output. Use Vite/webpack magic comments for naming and prefetch.

const Dashboard = defineAsyncComponent(
  () => import(/* webpackChunkName: 'dashboard' */ './Dashboard.vue')
);

Route-Level Lazy Loading

Vue Router supports async components directly in route definitions.

const routes = [
  { path: '/',          component: () => import('./Home.vue') },
  { path: '/dashboard', component: () => import('./Dashboard.vue') },
  { path: '/admin',     component: () => import('./Admin.vue') }
];

Suspense Component

Vue's experimental <Suspense> renders a fallback while any async dependency in its tree is pending.

<template>
  <Suspense>
    <template #default>
      <Dashboard />
    </template>
    <template #fallback>
      <LoadingSpinner />
    </template>
  </Suspense>
</template>

Async setup()

setup() can be async — Vue waits for it to resolve before rendering. Works inside Suspense.

<!-- AsyncPage.vue -->
<script setup>
import { useUserStore } from '@/stores/user';

const userStore = useUserStore();
await userStore.fetchProfile(); // top-level await
</script>

<!-- Parent uses Suspense to handle the await -->

Error Handling with onErrorCaptured

Use onErrorCaptured in a parent to catch errors from async components or async setup.

import { onErrorCaptured } from 'vue';

onErrorCaptured((err, instance, info) => {
  console.error('Caught:', err, info);
  return false; // prevent propagation
});

Combining Suspense and Error Boundary

Wrap an async component tree with both Suspense (for loading) and an error-catching ancestor (for errors).

<template>
  <ErrorBoundary>
    <Suspense>
      <template #default><Dashboard /></template>
      <template #fallback><Spinner /></template>
    </Suspense>
  </ErrorBoundary>
</template>

Suspense Is Experimental

As of Vue 3.4, <Suspense> is still marked experimental — the API may change. Async components themselves (defineAsyncComponent) are stable and production-ready.

When to Use Async Components

Good candidates: route components, admin/settings panels, modals opened on demand, heavy charts/editors. Skip for: tiny components, components used on every page (split them another way).

Quick Check

Which Vue 3 function turns a dynamic import into a lazy-loaded component?

Recap: Async Components & Suspense

defineAsyncComponent wraps dynamic imports for lazy loading. Configure loadingComponent, errorComponent, delay, timeout. Bundlers emit separate chunks. Route-level lazy loading via component: () => import('...'). <Suspense> renders fallback while async deps resolve (still experimental). Async setup() with top-level await works inside Suspense. onErrorCaptured catches async errors.

Frequently asked questions

Is the “Async Components and Suspense” lesson free?

Yes — the full text of “Async Components and Suspense” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.

What will I learn in “Async Components and Suspense”?

Load components lazily with defineAsyncComponent, wrap them in , and show fallback content while the component bundle loads. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Frontend Academy?

No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Async Components and Suspense” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Frontend Academy lesson?

Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. provide() and inject() for Dependency Injection
  2. Async Components and Suspense
  3. Custom Directives
  4. Plugin System: app.use()
← Back to Frontend Academy