Vue.js Teleport
Teleport moves a piece of a component's template to a different place in the page, while the logic stays in the original component. Modals, tooltips, dropdown menus, and notification banners need this power. These elements must appear above everything else, but the component that controls them often sits deep inside a layout with clipped edges or stacking rules that trap them.
Imagine ordering a parcel. You place the order from your sofa, and the parcel lands at a different address, such as your office. Teleport works the same way: the component places the order, and the content arrives at another spot in the page.
The Problem Teleport Solves
A modal inside a small card inherits the card's boundaries. A parent with overflow: hidden clips the modal. A parent with a transform changes how fixed positioning behaves. The modal ends up cut off or stuck behind other elements.
Diagram: A Trapped Modal
<body>
<div class="page">
<div class="card" (overflow hidden)>
<Modal /> <-- clipped by the card
</div>
</div>
</body>Use the Teleport Component
Wrap the content in <Teleport> and set the to attribute to a CSS selector. Vue renders the wrapped content inside that target.
<script setup>
import { ref } from "vue";
const open = ref(false);
</script>
<template>
<button @click="open = true">Open dialog</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal.</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
</template>The button stays inside the card. The modal markup appears as a direct child of <body>, free from the card's clipping rules.
Diagram: After Teleport
<body>
<div class="page">
<div class="card">
[Open dialog] button
</div>
</div>
<div class="modal"> ... </div> <-- teleported here
</body>Logic Stays with the Component
Teleport changes only where the HTML lands. Data, events, props, and methods still belong to the original component. The open variable above lives in the component, and the button inside the modal still changes it. Developer tools show the modal as part of the component tree, even though the browser places it elsewhere.
Choose a Target Container
The to value accepts any selector, such as body, #modal-root, or .notifications. The target must exist in the page before the teleport mounts. A dedicated container in your main HTML file keeps things tidy.
<!-- index.html -->
<body>
<div id="app"></div>
<div id="modal-root"></div>
</body>
<!-- Component -->
<Teleport to="#modal-root"> ... </Teleport>Turn Teleport Off with disabled
The disabled prop switches teleporting off and keeps the content in its original place. Mobile layouts sometimes want an inline panel, while desktop layouts want a floating one.
<Teleport to="body" :disabled="isMobile">
<div class="panel"> ... </div>
</Teleport>Several Teleports to One Target
Many components can teleport into the same container. Vue appends each one in mounting order.
<Teleport to="#notifications"><div>Message A</div></Teleport>
<Teleport to="#notifications"><div>Message B</div></Teleport>Diagram: Shared Target
Component 1 --Teleport--+
+--> #notifications: [Message A][Message B]
Component 2 --Teleport--+Good Uses for Teleport
- Modal dialogs and confirmation boxes
- Full-screen image viewers
- Toast notifications
- Tooltips and dropdowns that must escape clipped containers
Accessibility deserves attention with teleported dialogs. Add a clear title, move keyboard focus into the dialog on open, and return focus to the trigger button on close. Screen reader users then follow the page without confusion.
Summary
Teleport renders part of a template in another location of the page while the component keeps full control of the logic. The to attribute chooses the destination, disabled switches the behavior off, and several teleports can share one container. Modals, toasts, and tooltips gain reliable positioning because they escape the clipping and stacking rules of deeply nested parents.
