Teleporting in Vue 3
Tags: javascriptvue
Published: 2020-09-08 20:26:47

---
title: Teleporting in Vue 3
published: true
date: 2020-08-14 18:28:15 UTC
tags: javascriptvue
canonical_url: https://www.thisdot.co/blog/teleporting-in-vue-3
---

Teleporting is a new feature brought by the release of Vue 3, inspired by [React Portals](https://reactjs.org/docs/portals.html). The Vue team added this feature with the help of a third-party plugin named [Portal-Vue](https://github.com/LinusBorg/portal-vue).

Teleporting, as its name suggests, allows you to move an object from one place to another. In Vue JS terms, it allows you to define a component in one place, and render it in a different position in the DOM tree, even outside the scope of the Vue app.

In this article, I will introduce Teleporting, explain the need for it, and finally share a running example to see it in action.

## What is Teleporting?

A **Component** in Vue JS is a first-class citizen. It's encouraged to encapsulate UI, and related logic, into smaller components. These can be reused and nested, one inside another, to build a tree of components that make up your app UI.

Whereas some UI components are better placed towards the end of the `<body>` element, these require fixed or absolute positioning and management of their z-index. Modal Dialog, Notification UI or Popup UI are an example of this.

The motive behind placing them there is to simplify their styling, eliminate the need to write complex styles, and to deal with [z-index Stacking Context](https://philipwalton.com/articles/what-no-one-told-you-about-z-index/) behavior.

This is where Vue 3 Teleporting comes to the rescue, and reconciles between the two contradicting approaches forementioned. It allows you to maintain a nested tree of UI Components by letting you physically **define** a nested component inside one another. It works its magic to **render** the same component in a different position (different from where it was initially defined) in DOM, satisfying the best practices stated in the latter approach.

> You can read more on [Teleporting Use Cases](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0025-teleport.md#use-cases) on the official RFC page for this feature.

Teleporting can also render components in DOM outside the scope of the app. This opens the door to a number of possibilities that it can help with. For instance, Vue is used to build a certain section of the Page. With Teleporting, you can dynamically inject components into the DOM in other sections of the Page not belonging to the jurisdiction of the app. Think of it like Dashboards with widgets!

Now that you have an idea of what Teleporting is, let’s move on.

## Teleport Component

Vue 3 provides the `<teleport>` component, which creates all the magic behind Teleporting. This component has two useful properties:

**to** : This required property specifies the location in DOM where it renders the content of the `<teleport>` component. The value can be any of the following variations below:

- ID Selector: `<teleport to="#location-in-dom">...</teleport>`
- Class Selector: `<teleport to=".location-in-dom">...</teleport>`
- Data-Attr Selector: `<teleport to="[data-location-in-dom]">...</teleport>`
- Reactive Property: `<teleport :to="locationInDom">...</teleport>`

**disabled** : This optional property, when assigned a value of `true`, disables the state of the `<teleport>` component. In a disabled state, its content is rendered in the same location where it's defined. Only when the `disabled` property is assigned a value of `false`, which is the default, the content is rendered in the DOM location specified in the `to` property.

One important side-effect to mention in this context is when the `disabled` property is bound to a reactive property. Switching the value between `false` and `true` causes the component to render in two different DOM locations. While doing so, the state of the rendered content is preserved. For example, a `<teleport>` component hosts a `Video` player. When the `<teleport>` component is disabled, the video starts playing in its place. When the `disabled` property is switched to `false`, the video continues playing in the new DOM location.

Finally, when you want the `<teleport>` component to be disabled but not rendering anything to the DOM, you may use `v-if`, the Vue JS Directive, to hide it and prevent it from rendering.

## Vue 3 Modal Component

In this section, we will build a Vue Modal component. It will be used in the next section when I demonstrate the Teleporting feature.

There are countless numbers of Modal components out there. Instead of building ourselves a new one, I will make use of a Modal UI built by [Chris Coyier](https://twitter.com/chriscoyier), and introduced in his article on [Considerations for Styling a Modal](https://css-tricks.com/considerations-styling-modal/). This article is a valuable reference for building Modals as it explains every decision taken when building such a UI.

Here's a running example of the Modal UI on [CodePen - Modal](https://codepen.io/bilalhaidar/pen/JjXPVNJ)

Let's wrap it in a Vue JS component!

First of all, create a new Vue JS app by running these steps:

#### Step 1: Install @vue/cli NPM package

Open a terminal window and run the following command:

```
npm install -g @vue/cli
```

#### Step 2: Create a new Vue app

```
vue create vue-teleporting
```

Select the `default preset` when prompted.

#### Step 3: Run the app

Run the app to make sure it's working properly:

```
cd vue-teleporting
npm run serve
```

You should have a running app now!

#### Step 4: Build the Modal Component

Inside the `/src/components/` folder, add a new Vue component named `Modal.vue`. Replace its content with the following:

```
<template>
  <div>
    <div class="modal-overlay" id="modal-overlay"></div>
    <div
      class="modal"
      id="modal"
      role="dialog"
      aria-labelledby="modal-header"
      aria-describedby="modal-body"
    >
      <button
        class="close-button"
        id="close-button"
        aria-label="Close modal"
        @click.prevent="closeModal"
      >X</button>
      <div class="modal-container">
        <header class="modal-header" id="modal-header">
          <slot name="header">Header goes here ...</slot>
        </header>

        <section class="modal-body" id="modal-body">
          <slot name="body">Body goes here ...</slot>
        </section>

        <footer class="modal-footer">
          <slot name="footer">Footer goes here ...</slot>
        </footer>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "Modal",
  methods: {
    closeModal() {
      this.$emit("close-modal");
    },
  },
};
</script>
```

  
I've made a few changes to convert this UI component to a Vue component. The additions include:

Three new sections representing the header, body, and footer of the Modal component.

The use of `<slot>` components to add flexibility.

> You can read more about Slots in Vue JS by checking my article on [Content Distribution in Vue JS](https://labs.thisdot.co/blog/content-distribution-in-vue-js).

The use of a few new styling tags to cater for the new sections.

Making the Modal accessible by introducing a few **aria** attributes. For instance, the use of `role="dialog"` helps assistive software identify the Modal component as being a Dialog or Window that's separated from the rest of the app UI.

> You can read more about page accessibility by checking [Daniel Marin's](https://twitter.com/danm_t) article, [Make it Accessible: Better Layout with HTML](https://labs.thisdot.co/blog/make-it-accessible-better-layout-with-html)

The `Close` button now emits the `modal-close` event to send a request to the parent component hosting this Modal to eventually close it.

Let's embed this new component inside the `App.vue` and run the app.

```
<template>
  <Modal @close-modal="closeModal">
    <template #body>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae expedita corrupti laudantium aperiam, doloremque explicabo ipsum earum dicta saepe delectus totam vitae ipsam doloribus et obcaecati facilis eius assumenda, cumque.</p>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae expedita corrupti laudantium aperiam, doloremque explicabo ipsum earum dicta saepe delectus totam vitae ipsam doloribus et obcaecati facilis eius assumenda, cumque.</p>
    </template>
  </Modal>
</template>
```

  
\_\_Figure 1\_\_ below shows the app running with the Modal component open.

[![vue-teleporting-1](https://images.ctfassets.net/zojzzdop0fzx/3qXs1k2DEo8vwgaK4PuaN/c19b662f108dcc97ec896a1bb0f1a5da/1.png)](https://images.ctfassets.net/zojzzdop0fzx/3qXs1k2DEo8vwgaK4PuaN/c19b662f108dcc97ec896a1bb0f1a5da/1.png)

That's it! Now we have a Modal Vue component.

Let's make use of this component in the next section.

## Teleporting in Action

So far we have been using Vue 2 to build a Modal component. Now it's time to add the Vue 3 bits into our app and use Teleporting!

Open a terminal window and navigate to the root folder of our app. Then, run the following command to convert our Vue 2 app into a Vue 3 one.

```
vue add vue-next
```

  
Open the `/package.json` file and make sure the `dependencies` and `dev-dependencies` sections are similar to this below:

```
 "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^3.0.0-rc.5"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.4.0",
    "@vue/cli-plugin-eslint": "~4.4.0",
    "@vue/cli-service": "~4.4.0",
    "@vue/compiler-sfc": "^3.0.0-rc.5",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^7.0.0-alpha.0",
    "vue-cli-plugin-vue-next": "~0.1.3"
  },
```

  
The \_\_vue-next\_\_ package automatically converts it for you.

Switch back to the `App.vue` and replace its content with:

```
<template>
  <div>
    <h1>Demonstrating Teleporting in Vue 3</h1>
    <teleport to="#modal-area">
      <Modal @close-modal="closeModal">
        <template #body>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae expedita corrupti laudantium aperiam, doloremque explicabo ipsum earum dicta saepe delectus totam vitae ipsam doloribus et obcaecati facilis eius assumenda, cumque.</p>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae expedita corrupti laudantium aperiam, doloremque explicabo ipsum earum dicta saepe delectus totam vitae ipsam doloribus et obcaecati facilis eius assumenda, cumque.</p>
        </template>
      </Modal>
    </teleport>
  </div>
</template>

<script>
import Modal from "./components/Modal.vue";

export default {
  name: "App",
  components: {
    Modal,
  },
  methods: {
    closeModal() {},
  },
};
</script>
```

  
The component now uses a `<teleport>` component to wrap the Modal component. The `to` property is now pointed to a `<div>` element located in the `/public/index.html` file. 
<p>Replace the contents of the <code>/public/index.html</code> file with the following:</p>
<div data-language="html"><pre><code><span>&lt;!DOCTYPE html&gt;</span>
<span><span><span>&lt;</span>html</span> <span>lang</span><span><span>=</span><span>"</span>en<span>"</span></span><span>&gt;</span></span>

<span><span><span>&lt;</span>head</span><span>&gt;</span></span>
  <span><span><span>&lt;</span>meta</span> <span>charset</span><span><span>=</span><span>"</span>utf-8<span>"</span></span><span>&gt;</span></span>
  <span><span><span>&lt;</span>meta</span> <span>http-equiv</span><span><span>=</span><span>"</span>X-UA-Compatible<span>"</span></span> <span>content</span><span><span>=</span><span>"</span>IE=edge<span>"</span></span><span>&gt;</span></span>
  <span><span><span>&lt;</span>meta</span> <span>name</span><span><span>=</span><span>"</span>viewport<span>"</span></span> <span>content</span><span><span>=</span><span>"</span>width=device-width,initial-scale=1.0<span>"</span></span><span>&gt;</span></span>
  <span><span><span>&lt;</span>link</span> <span>rel</span><span><span>=</span><span>"</span>icon<span>"</span></span> <span>href</span><span><span>=</span><span>"</span>&lt;%= BASE_URL %&gt;favicon.ico<span>"</span></span><span>&gt;</span></span>
  <span><span><span>&lt;</span>title</span><span>&gt;</span></span>&lt;%= htmlWebpackPlugin.options.title %&gt;<span><span><span>&lt;/</span>title</span><span>&gt;</span></span>
<span><span><span>&lt;/</span>head</span><span>&gt;</span></span>

<span><span><span>&lt;</span>body</span><span>&gt;</span></span>
  <span><span><span>&lt;</span>noscript</span><span>&gt;</span></span>
    <span><span><span>&lt;</span>strong</span><span>&gt;</span></span>We're sorry but &lt;%= htmlWebpackPlugin.options.title %&gt; doesn't work properly without JavaScript enabled.
      Please enable it to continue.<span><span><span>&lt;/</span>strong</span><span>&gt;</span></span>
  <span><span><span>&lt;/</span>noscript</span><span>&gt;</span></span>
  <span><span><span>&lt;</span>div</span> <span>id</span><span><span>=</span><span>"</span>app<span>"</span></span><span>&gt;</span></span><span><span><span>&lt;/</span>div</span><span>&gt;</span></span>
  <span><span><span>&lt;</span>div</span> <span>id</span><span><span>=</span><span>"</span>modal-area<span>"</span></span><span>&gt;</span></span><span><span><span>&lt;/</span>div</span><span>&gt;</span></span>
  <span>&lt;!-- built files will be auto injected --&gt;</span>
<span><span><span>&lt;/</span>body</span><span>&gt;</span></span>

<span><span><span>&lt;/</span>html</span><span>&gt;</span></span></code></pre></div>
<br>
Let\s run the app and see it in action!
<p><strong>Figure 2</strong> below shows the Vue 3 app running.
<a href="https://images.ctfassets.net/zojzzdop0fzx/JMMs7Ih1UdImeQrznoVaE/4591c30b359397ce493073aae2511005/2.png" style="display: block" target="_blank" rel="noopener">
          <span style="position: relative; display: block; ; max-width: 768px; margin-left: auto; margin-right: auto;">
        <span style="padding-bottom: 60.19938650306749%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,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'); background-size: cover; display: block;">
          <img style="width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;" alt="vue-teleporting-2" title="" src="https://images.ctfassets.net/zojzzdop0fzx/JMMs7Ih1UdImeQrznoVaE/4591c30b359397ce493073aae2511005/2.png" srcset="https://images.ctfassets.net/zojzzdop0fzx/JMMs7Ih1UdImeQrznoVaE/4591c30b359397ce493073aae2511005/2.png?w=652 652w,
https://images.ctfassets.net/zojzzdop0fzx/JMMs7Ih1UdImeQrznoVaE/4591c30b359397ce493073aae2511005/2.png?w=1304 1304w,
https://images.ctfassets.net/zojzzdop0fzx/JMMs7Ih1UdImeQrznoVaE/4591c30b359397ce493073aae2511005/2.png?w=2608 2608w" sizes="(max-width: 768px) 100vw, 768px" loading="lazy">
        </span>
      </span>
        </a></p>
<p>The Modal component is now rendered inside the <code>&lt;div id=”modal-area”/&gt;</code> outside the scope of the Vue 3 app and inside the <code>&lt;body&gt;</code> element of the <code>index.html</code> page.</p>
<p>You are nesting the Modal component inside the <code>App.vue</code> and Vue 3 will render it inside the <code>&lt;body&gt;</code> tag. Have your cake and eat it too!</p>
<p>How to close the Modal and remove its content from the DOM?</p>
<p>Let's make use of the <code>disabled</code> property of the <code>&lt;teleport&gt;</code> component and also the <code>v-if</code> Vue directive to close the Modal, and remove the component from the DOM.</p>
<div data-language="text"><pre><code>&lt;template&gt;
  &lt;div&gt;
    &lt;h1&gt;Demonstrating Teleporting in Vue 3&lt;/h1&gt;
    &lt;teleport to="#modal-area" :disabled="shouldClose" v-if="!shouldClose"&gt;
      &lt;Modal @close-modal="closeModal"&gt;
        &lt;template #body&gt;
          ...
        &lt;/template&gt;
      &lt;/Modal&gt;
    &lt;/teleport&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import Modal from "./components/Modal.vue";

export default {
  name: "App",
  components: {
    Modal,
  },
  data() {
    return {
      shouldClose: false,
    };
  },
  methods: {
    closeModal() {
      this.shouldClose = true;
    },
  },
};
&lt;/script&gt;</code></pre></div>
<br>
The code introduces a new reactive property named `shouldClose`. This property is assigned a value of `true` by default. 
<p>The code assigns the <code>shouldClose</code> property to the <code>disabled</code> property on the <code>&lt;teleport&gt;</code> component <code>:disabled=”shouldClose”</code>. As long as the value of <code>shouldClose</code> is false, the <code>&lt;teleport&gt;</code> component won't be disabled, hence it will render its content to the DOM.</p>
<p>When the user clicks the <code>Close</code> button, the button's event handler sets the value of <code>shouldClose</code> property to <code>true</code>. The <code>&lt;teleport&gt;</code> reactively stops rendering inside the <code>&lt;body&gt;</code> tag. However, it tries to render the Modal component in the position where it was originally defined- that is, inside the <code>App.vue</code> component. </p>
<p>Our goal is to prevent the <code>&lt;teleport&gt;</code> component from rendering anything to the DOM. We can achieve that by using the <code>v-if=”!shouldClose”</code> directive expression on the <code>&lt;teleport&gt;</code> component. This instructs the <code>&lt;teleport&gt;</code> component not to render its content anywhere inside the DOM.</p>
<p>That's all!</p>
<h2>Conclusion</h2>
<p>This article introduced the Teleporting feature coming along with Vue 3. </p>
<p>I hope you appreciate the importance and value of such an addition on the Vue framework. This will allow building app UIs to the standards and best practices of both the Vue framework, as well as UI building and design.</p>
</div></teleport>