Javascript is required
Michael Hoffmann LogoMichael Hoffmann

Vue Tip: Effortless Handle Asynchronous Components With Suspense

Michael Hoffmann - Senior Frontend Developer (Freelancer)
Michael Hoffmann
Apr 28, 2023
2 min read
Vue Tip: Effortless Handle Asynchronous Components With Suspense Image
Experimental Feature

<Suspense> is an experimental feature. It is not guaranteed to reach stable status and the API may change before it does.

In the world of modern web development, delivering seamless user experiences is of utmost importance. Asynchronous components, which load data or resources dynamically, play a crucial role in achieving this goal.

Vue 3 introduces an interesting feature called Suspense to effortlessly handle asynchronous components. You can use it to show a loading state while all async dependencies in the nested component tree are being resolved.

Without Suspense

For this article, let's use the following component hierarchy for demonstration purposes:

  ├─ <Profile> (async component)
  └─ <FetchComponent> (component with async setup())

We have a Dashboard component that includes two child components:

  • Profile: This component is loaded asynchronously
  • FetchComponent: This component fetches data using async/await within <script setup>

Each of the child components has its loading state, they are visible if the component is loaded:

Without Suspense


The code for this demo is available at StackBlitz:

As you can imagine this can harm the user experience if you see multiple loading spinners on the page and content displayed at different times.

Let's take a look at how <Suspense> is trying to solve that problem.

With Suspense

The <Suspense> component enables us to display top-level loading & error states while waiting for nested async dependencies to be resolved:

    <!-- Component with nested async dependencies -->
    <Dashboard />

    <!-- Loading state via #fallback slot -->
    <template #fallback> Loading... </template>

<Suspense> has two slots: #default and #fallback. Both slots only allow for one immediate child node.

Our demo application now behaves this way using <Suspense>:

With Suspense

The <Suspense> component has two states: pending and resolve.

When <Suspense> is first rendered, it will display its default slot content in memory. If any asynchronous dependencies are encountered, it will transition to a pending state. While in the pending state, the fallback content will be shown. Once all the encountered asynchronous dependencies are resolved, <Suspense> enters a resolved state and displays the resolved default slot content.

If no async dependencies were encountered during the initial render, <Suspense> will directly go into a resolved state.


<Suspense> currently does not provide error handling via the component itself. However, you can use the errorCaptured option or the onErrorCaptured() hook to capture and handle async errors in the parent component of <Suspense>.

If you liked this Vue tip, follow me on Twitter to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue newsletter:

New Vue & Nuxt tips delivered to your inbox:
I will never share any of your personal data.