Skip to content
← Todas las preguntas
⚡ Intermedio

¿Cómo funciona Suspense con componentes asíncronos?

🧩 Componentes🎰 Slots⏳ Suspense🌀 Teleport

<Suspense> es un componente integrado que renderiza contenido de fallback mientras sus hijos asíncronos se resuelven. Funciona con dos tipos de dependencias asíncronas: componentes asíncronos (defineAsyncComponent) y componentes con un async setup().

Uso básico

vue
<template>
  <Suspense>
    <!-- Slot por defecto: el contenido asíncrono -->
    <AsyncDashboard />

    <!-- Slot fallback: se muestra mientras carga -->
    <template #fallback>
      <LoadingSkeleton />
    </template>
  </Suspense>
</template>
Open in Vue Playground

Suspense espera a que todas las dependencias asíncronas dentro del slot por defecto se resuelvan antes de cambiar del fallback al contenido real.

Async setup

Un componente con async setup() (usando await de nivel superior en <script setup>) es automáticamente una dependencia de Suspense:

vue
<!-- UserProfile.vue -->
<script setup>
const user = await fetchUser() // await de nivel superior
const posts = await fetchPosts(user.id)
</script>

<template>
  <h1>{{ user.name }}</h1>
  <PostList :posts="posts" />
</template>
Open in Vue Playground
vue
<!-- Parent.vue -->
<template>
  <Suspense>
    <UserProfile />
    <template #fallback>Cargando perfil...</template>
  </Suspense>
</template>
Open in Vue Playground

Un único elemento raíz en cada slot

Suspense rastrea un único hijo inmediato por slot. Envuelve varios elementos:

vue
<template>
  <Suspense>
    <div>
      <AsyncHeader />
      <AsyncContent />
    </div>

    <template #fallback>
      <div>
        <LoadingSpinner />
        <p>Cargando...</p>
      </div>
    </template>
  </Suspense>
</template>
Open in Vue Playground

Timeout para re-activaciones

Cuando Suspense ya está resuelto y comienza nuevo trabajo asíncrono (por ejemplo, al cambiar de vista), el contenido anterior permanece visible hasta que expira un timeout. Configura timeout para controlar cuándo vuelve a aparecer el fallback:

vue
<template>
  <!-- Muestra el fallback tras 200ms si la nueva vista no ha resuelto -->
  <Suspense :timeout="200">
    <component :is="currentView" :key="currentView" />
    <template #fallback>Cargando...</template>
  </Suspense>
</template>
Open in Vue Playground

Eventos de Suspense

Controla el estado de carga programáticamente con @pending, @resolve y @fallback:

vue
<script setup>
import { ref } from 'vue'
const isLoading = ref(false)
</script>

<template>
  <ProgressBar v-if="isLoading" />

  <Suspense @pending="isLoading = true" @resolve="isLoading = false">
    <AsyncPage />
    <template #fallback><PageSkeleton /></template>
  </Suspense>
</template>
Open in Vue Playground

Anidamiento con RouterView, Transition y KeepAlive

El orden correcto de anidamiento es RouterView, luego Transition, luego KeepAlive y luego Suspense:

vue
<template>
  <RouterView v-slot="{ Component }">
    <Transition mode="out-in">
      <KeepAlive>
        <Suspense>
          <component :is="Component" />
          <template #fallback>Cargando...</template>
        </Suspense>
      </KeepAlive>
    </Transition>
  </RouterView>
</template>
Open in Vue Playground

Suspense sigue siendo experimental

Suspense sigue siendo una API experimental. Funciona, pero la API podría cambiar en versiones futuras. En producción, mantén los límites Suspense al mínimo y documenta dónde los usas.

Ver también: ¿Qué son los componentes asíncronos? · ¿Qué son Teleport, Fragments y Suspense? · ¿Cómo funciona el manejo de errores?

Referencias

Publicado bajo la licencia MIT.