March 9, 2024

Understanding <Teleport> in Vue 3 With Examples

Understanding <Teleport> in Vue 3 With Examples

Vue <Teleport> is a built-in functionality in Vue 3 that allows you to render a component’s content in a different part of the DOM tree, while keeping the component’s logic and state within its original place in the component hierarchy.

This can be incredibly useful when you need to create modals, tooltips, or any other UI elements that should be positioned or rendered outside of their parent component’s scope.

For example, imagine you have a complex component tree, and you need to display a modal dialog on top of everything else. Without Vue Teleport, you would have to pass down the modal state and props through multiple levels of components, making your code harder to maintain and reason about. With Vue Teleport, you can simply render the modal content directly in the desired location (e.g., the document body), while keeping the modal component’s logic and state management within its original component.

In this blog post, we’ll dive deeper into the Vue Teleport feature, explore how it works under the hood, and provide real-life examples to help you understand and implement it effectively in your Vue 3 applications.

What is Vue Teleport?

Vue Teleport is a built-in feature in Vue 3 that allows you to render a part of a component’s template in a different location in the DOM tree. This is achieved using a special <Teleport> component.

Under the hood, the <Teleport> component works by rendering its content as a child of the specified target element. The target element can be any valid CSS selector or an actual DOM node object.

Example with Vue Teleport

Here’s a basic example of how the <Teleport> component is used:

<Teleport to="body">
<div>
This content will be rendered as a child of the <body> element.
</div>
</Teleport>

In this example, the <Teleport> component is used to render its content (a <div> element) as a child of the <body> element, even though the <Teleport> component itself is placed elsewhere in the component hierarchy.

Example without Vue Teleport

Without Vue Teleport, you would need to manually manage the positioning and rendering of UI elements like modals or tooltips using JavaScript and CSS. Here’s an example of how you might create a modal dialog without Vue Teleport:

<template>
<div>
<!-- Other content -->
<div v-if="showModal" class="modal">
<div class="modal-content">
<!-- Modal content -->
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
const showModal = ref(false);
onMounted(() => {
// Add event listeners to show/hide modal
// Manually position and style the modal
});
onBeforeUnmount(() => {
// Remove event listeners
});
</script>

Using Vue Teleport

Using Vue Teleport in your Vue 3 application is straightforward. You can either use it directly in your component’s template or set it up programmatically in your component’s script.

Using Vue Teleport in the Template

To use Vue Teleport in your component’s template, you simply need to wrap the content you want to render elsewhere with the <Teleport> component. The to prop specifies the target element where the content will be rendered.

Here’s an example:

<template>
<div>
<!-- Content rendered in the component's regular location -->
<h1>My Component</h1>
<p>This content is rendered inside the component.</p>
<!-- Content rendered in the document body -->
<Teleport to="body">
<div class="modal">
<h2>Modal Dialog</h2>
<p>This modal content is rendered in the document body.</p>
</div>
</Teleport>
</div>
</template>

In this example, the modal dialog content is wrapped in the <Teleport> component with to="body", which means it will be rendered as a child of the <body> element, even though the <Teleport> component itself is inside the component’s template.

Using Vue Teleport Programmatically Alternatively, you can use Vue Teleport programmatically by importing the createTeleport function from Vue and creating a Teleport component instance in your component’s script.

<template>
<div>
<!-- Content rendered in the component's regular location -->
<h1>My Component</h1>
<p>This content is rendered inside the component.</p>
<!-- Render target for the teleported content -->
<teleport to="#modal-container">
<div class="modal">Modal Content</div>
</teleport>
</div>
</template>
<script setup>
import { createTeleport } from "vue";
const targetElement = document.getElementById("modal-container");
const modalComponent = createTeleport(
'<div class="modal">Modal Content</div>',
);
targetElement.appendChild(modalComponent.$el);
</script>

In this example, we create a <div> element with the ID modal-container in the component’s template. In the mounted lifecycle hook, we get a reference to this element using document.getElementById. We then create a Teleport component instance using createTeleport and pass in the content we want to render. Finally, we append the Teleport component’s root element (modalComponent.$el) to the target element (targetElement).

Using Vue Teleport programmatically can be useful when you need more control over when and where the content is rendered, or when you need to dynamically create and render multiple instances of a component.

Real-Life Examples of Using Vue Teleport

Now that we understand what Vue Teleport is and how to use it, let’s explore some real-life examples to solidify our understanding.

Example 1: Creating a Modal Dialog

One of the most common use cases for Vue Teleport is creating modal dialogs. Modals are UI components that appear on top of the main content and require the user’s attention before they can interact with the rest of the application.

Without Vue Teleport, creating modals can be challenging, as you would need to manually manage the positioning and z-index of the modal element to ensure it appears on top of everything else. Additionally, you would need to handle edge cases like scrolling and body overflow.

With Vue Teleport, creating modals becomes much easier. Here’s an example:

<template>
<div>
<!-- Main content -->
<button @click="showModal = true">Show Modal</button>
<!-- Modal content rendered in the document body -->
<Teleport to="body">
<div v-if="showModal" class="modal">
<div class="modal-content">
<h2>Modal Dialog</h2>
<p>This is a modal dialog rendered using Vue Teleport.</p>
<button @click="showModal = false">Close</button>
</div>
</div>
</Teleport>
</div>
</template>
<script setup>
import { ref } from 'vue';
const showModal = ref(false);
</script>
<style>
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 4px;
}
</style>

In this example, we use Vue Teleport to render the modal content as a child of the <body> element. This ensures that the modal appears on top of all other content, regardless of the component hierarchy.

We also use some CSS to position the modal in the center of the screen and add a semi-transparent background overlay.

When the “Show Modal” button is clicked, the showModal ref data property is set to true, which causes the modal content to be rendered using Vue Teleport. When the “Close” button is clicked, the showModal data property is set to false, which removes the modal content from the DOM.

Disabling Teleport

In some cases, you may want to disable the Teleport behavior and render the content in its original position within the component hierarchy. Vue 3 provides a way to do this by setting the disabled prop on the <Teleport> component to true.

Here’s an example:

<template>
<div>
<!-- Content rendered in the component's regular location -->
<h1>My Component</h1>
<p>This content is rendered inside the component.</p>
<!-- Teleport behavior disabled -->
<Teleport to="body" :disabled="isTeleportDisabled">
<div class="modal">
<h2>Modal Dialog</h2>
<p>This modal content will be rendered in its original position.</p>
</div>
</Teleport>
</div>
</template>
<script setup>
import { ref } from 'vue';
const isTeleportDisabled = ref(true);
</script>

In this example, we set the isTeleportDisabled data property to true, which causes the disabled prop on the <Teleport> component to be true. As a result, the modal content will be rendered in its original position within the component hierarchy, instead of being teleported to the <body> element.

Disabling Teleport can be useful in several scenarios:

  • Conditional Rendering: You may want to disable Teleport based on certain conditions, such as the viewport size or user preferences.
  • Fallback Behavior: Teleport may not work as expected in some environments (e.g., older browsers or server-side rendering), so you can disable it and provide a fallback behavior.
  • Testing and Debugging: Disabling Teleport can be helpful when testing or debugging your application, as it allows you to inspect the component’s content in its original position.

It’s important to note that when Teleport is disabled, the content will be rendered in its original position within the component hierarchy, which may affect layout and styling. Therefore, you should ensure that your component’s styles and layout are compatible with both teleported and non-teleported content.

Multiple Teleports on the Same Target

In some cases, you may need to render multiple components or pieces of content in the same target element using Teleport. Vue 3 allows you to do this by using multiple <Teleport> components with the same to prop value.

A common use case for this is rendering modals, tooltips, and other UI elements directly into the <body> element, so that they appear on top of the main content.

Here’s an example:

<template>
<div>
<!-- Content rendered in the component's regular location -->
<h1>My Component</h1>
<p>This content is rendered inside the component.</p>
<!-- Teleport multiple pieces of content to the body -->
<Teleport to="body">
<div class="modal">
<h2>Modal Dialog</h2>
<p>This is a modal dialog rendered using Teleport.</p>
</div>
</Teleport>
<Teleport to="body">
<div class="tooltip">This is a tooltip rendered using Teleport.</div>
</Teleport>
</div>
</template>

In this example, we have two <Teleport> components, both with the to="body" prop, which means that their content will be rendered as children of the <body> element.

The first <Teleport> component renders a modal dialog, and the second <Teleport> component renders a tooltip.

When using multiple Teleports on the same target, Vue will append the teleported content in the order in which the <Teleport> components are rendered. In this case, the modal content will be rendered first, followed by the tooltip content.

It’s important to note that when using multiple Teleports on the same target, you may need to manage the positioning and styling of the teleported content manually to avoid conflicts or overlapping elements. You can use CSS properties like position, z-index, and other positioning techniques to ensure that the teleported content is displayed correctly.

Using multiple Teleports on the same target can be useful in scenarios where you need to render different types of content or components in a specific location on the page, such as rendering a modal dialog and a tooltip within the <body> element.

References

Vue 3 Teleport Documentation. Link