Where Should Modals and Tooltips Render?
In Vue, components usually render inside their containers (parent elements). But for modals, tooltips, or dropdowns, this default can cause issues. These UI elements should often be independent of page hierarchy, unaffected by CSS like z-index or overflow.
Enter Vue 3’s handy feature: Teleport.
With Teleport, you can declare a component in one place and render it somewhere else in the DOM. In short: “I write it here, but show it there!”
What Is Teleport?
<teleport> is a special Vue component. It accepts a to prop that defines where the content should be moved.<template>
<teleport to="body">
<div class="modal">Hello World 🌍</div>
</teleport>
</template>
Here, the modal is moved to the document’s body. That way it avoids constraints like overflow: hidden or parent positioning quirks.
In short: Teleport is a portal inside Vue—the content looks the same, but its DOM location changes.
Why Do We Need It?
A modal rendered under its parent might:- Disappear due to
overflow: hidden. - Lose z-index battles and hide behind other elements.
- Misalign because of parent
position: relativecontainers.
body level sidesteps these constraints—great for complex layouts where “the tooltip doesn’t show” bugs pop up.A Simple Modal Example
<template>
<button @click="show = true">Open Modal</button>
<teleport to="body">
<div v-if="show" class="modal-backdrop" @click="show = false">
<div class="modal" @click.stop>
<h2>Teleport Modal</h2>
<p>This modal was moved to the body!</p>
</div>
</div>
</teleport>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(false)
</script>
<style>
.modal-backdrop {
position: fixed;
top: 0; left: 0;
width: 100vw; height: 100vh;
background: rgba(0,0,0,0.5);
}
.modal {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 2rem;
border-radius: 8px;
}
</style>
The modal is declared in the component but Vue moves it under body. Logically it remains in the same component; visually it appears on the top layer.The to Prop in Detail
to can be a CSS selector or a DOM element reference:<teleport to="#portal-target">
<MyTooltip />
</teleport>
orconst el = document.querySelector('#modals')
<teleport :to="el">
<MyModal />
</teleport>
If the target element doesn’t exist, Vue warns—ensure the target is present before render.Reactivity Still Works Inside Teleport
Teleported content remains reactive. Teleport only moves DOM placement—Vue reactivity stays the same.<teleport to="body">
<p>{{ message }}</p>
</teleport>
Updating message in the same component will update the teleported content as well.So Teleport is a “render portal,” not a “reactivity portal”—reactive bindings stay where they are.
Advanced Uses
1) Multiple Teleports
Use more than one Teleport to organize UI layers:<teleport to="#tooltips">
<MyTooltip />
</teleport>
<teleport to="#modals">
<MyModal />
</teleport>
This helps large apps with UI layer separation.2) Teleport with Transition
Animate modals cleanly by combining withTransition:<teleport to="body">
<transition name="fade">
<Modal v-if="open" />
</transition>
</teleport>
Transitions work independently of where the content is mounted.3) SSR Support
Teleport works with SSR, too. Vue renders a placeholder during SSR and moves the content to the right place on hydration.Common Pitfalls
- Missing Teleport target (
to)—content won’t appear. - Modals under parents with
overflow:hidden—use Teleport. - Solve z-index conflicts by giving the teleported layer its own stacking styles.
- Scoped CSS doesn’t apply inside teleported content—use
:deep()if needed.
Conclusion
Teleport is one of Vue’s most practical tools for rendering modern UI components in the right place. Modals, tooltips, context menus, and dropdowns can now avoid overflow and stacking issues.In short:
teleport to="body"→ move to the top of the DOM- Reactivity is preserved; only render location changes
- Improves layout sanity and accessibility in complex UIs