Skip to main content
Web DevelopmentTutorials

Vue 3 Teleport and Suspense: Advanced Component Patterns

Master Vue 3's built-in components for modals, loading states, and async component coordination with Teleport and Suspense.

Mbeah Essilfie

Mbeah Essilfie

May 24, 2026 at 06:03 AM

8 min read 1563 views
Vue 3 Teleport and Suspense: Advanced Component Patterns

Teleport: Render Anywhere in the DOM

Teleport renders a component's template in a different DOM location while keeping it in the same logical component tree.

<template>
  <div class="relative">
    <button @click="showModal = true">Open Modal</button>

    <!-- Renders at document body, not inside this div -->
    <Teleport to="body">
      <div v-if="showModal" class="fixed inset-0 z-50 flex items-center justify-center">
        <div class="fixed inset-0 bg-black/50" @click="showModal = false" />
        <div class="relative bg-white rounded-xl p-6 max-w-md w-full">
          <h2 class="text-lg font-bold">Modal Title</h2>
          <p>This renders at the body level, avoiding z-index issues.</p>
          <button @click="showModal = false">Close</button>
        </div>
      </div>
    </Teleport>
  </div>
</template>

<script setup lang="ts">
const showModal = ref(false)
</script>

Why Teleport?

  • Avoids overflow: hidden clipping on parent containers
  • Fixes z-index stacking context issues
  • Keeps component logic colocated with its trigger

Suspense: Async Component Coordination

Suspense shows fallback content while async components or setup functions resolve:

<template>
  <Suspense>
    <template #default>
      <AsyncDashboard />
    </template>
    <template #fallback>
      <DashboardSkeleton />
    </template>
  </Suspense>
</template>

The async component:

<!-- AsyncDashboard.vue -->
<script setup lang="ts">
// This makes the component "async" — Suspense will wait for it
const { data: stats } = await useFetch('/api/dashboard/stats')
const { data: activity } = await useFetch('/api/dashboard/activity')
</script>

<template>
  <div class="grid grid-cols-3 gap-4">
    <StatCard v-for="stat in stats" :key="stat.id" v-bind="stat" />
  </div>
  <ActivityFeed :items="activity" />
</template>
With Suspense, you can have multiple async components that all resolve before showing anything — no more cascading loading spinners.

Error Handling with Suspense

<script setup lang="ts">
const error = ref<Error | null>(null)

function onError(e: Error) {
  error.value = e
}
</script>

<template>
  <div v-if="error" class="text-red-500">
    Something went wrong: {{ error.message }}
  </div>

  <Suspense v-else @error="onError">
    <template #default>
      <AsyncContent />
    </template>
    <template #fallback>
      <LoadingSpinner />
    </template>
  </Suspense>
</template>

Combining Teleport + Suspense

<template>
  <Teleport to="#notification-area">
    <Suspense>
      <template #default>
        <AsyncNotifications />
      </template>
      <template #fallback>
        <NotificationSkeleton />
      </template>
    </Suspense>
  </Teleport>
</template>

These built-in components solve real layout and async coordination problems. Use them to build smoother user experiences.

TypeScriptVue
Mbeah Essilfie

Written by Mbeah Essilfie

Fullstack Software Developer

Read next

The Complete Guide to Vue 3 Composables
12 min read

The Complete Guide to Vue 3 Composables

Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.

Mbeah EssilfieMbeah Essilfie
985