Guía práctica de Vue 3: Cómo usar <Teleport> para modales, tooltips y notificaciones adaptativas

¿Sabías que puedes mover parte del DOM de un componente Vue a otro lugar del HTML sin romper la reactividad? En esta guía aprenderás cómo usar <Teleport> en Vue 3, desde lo básico hasta casos reales como notificaciones, menús contextuales y overlays.

¿Qué es <Teleport>?

<Teleport> es un componente integrado de Vue 3 que te permite renderizar una parte del DOM de tu componente en otro lugar del documento HTML, sin romper la lógica del componente ni su reactividad.

Imagina que tienes un modal, una notificación, o un menú que visualmente necesita estar en el <body> o en un contenedor global, pero lógicamente pertenece a un componente específico. Aquí es donde entra <Teleport>.

¿Cómo usar <Teleport>? – La forma básica

La estructura es muy simple:

<script setup lang="ts"></script>

<template>
  <Teleport to="body">
    <!-- contenido que será movido al <body> -->
  </Teleport>
</template>
  • El prop to acepta un selector CSS o un nodo del DOM.

  • Todo lo que esté dentro del <Teleport> se renderiza en ese destino.

  • Se mantiene el contexto del componente padre: props, eventos e inyección siguen funcionando igual.

Casos de uso prácticos

Notificaciones globales

Estructura base:

<!-- index.html -->
<div id="app"></div>
<div id="notifications"></div>

Componente NotificationManager.vue

<script setup lang="ts">
import { ref } from 'vue'
const notes = ref([])

function addNotification(text) {
  notes.value.push({ id: Date.now(), text })
  setTimeout(() => notes.value.shift(), 3000)
}

defineExpose({ addNotification }) // permite usarlo desde refs
</script>

<template>
  <Teleport to="#notifications">
    <div v-for="n in notes" class="note">{{ n }}</div>
  </Teleport>
</template>

Uso en otro componente:

<script setup lang="ts">
import { useTemplateRef } from 'vue'
import NotificationManager from './NotificationManager.vue'

const notifRef = useTemplateRef('notifRef')

function handleAction() {
  notifRef.value?.addNotification('¡Acción exitosa!')
}
</script>

<template>
  <NotificationManager ref="notifRef" />
  <button @click="handleAction">Guardar</button>
</template>

Comportamiento adaptativo con disabled:

Puedes condicionar el teleport para ciertos dispositivos (ej. overlay en desktop, inline en mobile):

<script setup lang="ts">
import { useMediaQuery } from '@vueuse/core'

// Define isMobile usando media query reactiva
const isMobile = useMediaQuery('(max-width: 768px)')
</script>

<template>
 <Teleport :disabled="isMobile" to="body">
   <div class="tooltip">Ayuda</div>
 </Teleport>
</template>

¿Cómo funciona?:

  • useMediaQuery('(max-width: 768px)') retorna un ref<boolean> reactivo que se actualiza automáticamente cuando cambia el tamaño de la pantalla

  • Si isMobile.value === true (pantallas menores a 768px), <Teleport> se desactiva y el tooltip se muestra inline en el flujo del componente.

  • Si isMobile.value === false, el tooltip se teletransporta al <body>, ideal para overlays en escritorio.

  • <Teleport> se adapta automáticamente sin recarga ni lógica adicional.

Consideraciones importantes

  • El destino (to) debe existir en el DOM cuando el <Teleport> se monta.

  • Si el destino no está disponible aún, usa defer.

  • No afecta el árbol de componentes en Vue: los componentes teletransportados siguen siendo hijos del componente que los contiene.

Extra: Transiciones animadas

Puedes usar <Transition> dentro del <Teleport> para animaciones fluidas.

<script setup lang="ts">

</script>

<template>
 <Teleport to="body">
   <Transition name="fade">
     <div class="modal">...</div>
   </Transition>
 </Teleport>
</template>

Conclusión

<Teleport> es una herramienta poderosa para separar la lógica de tus componentes del posicionamiento en el DOM. Con to, disabled y defer, puedes adaptarlo a múltiples escenarios, desde modales simples hasta comportamientos complejos y contextuales.