¿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
toacepta 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 unref<boolean>reactivo que se actualiza automáticamente cuando cambia el tamaño de la pantallaSi
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.
Sigue leyendo
Una idea lleva a otra.
)
¿Cómo integrar Tailwind 4 en Vue.js 3 + Vite?
)
¿Y si Vue.js también hablara Web Components? La magia de defineCustomElement
)
)