Teleport

Vue encourages us to build our UIs by encapsulating UI and related behavior into components. We can nest them inside one another to build a tree that makes up an application UI.

However, sometimes a part of a component’s template belongs into this component logically, while from a technical point of view, it would be preferable to move this part of the template somewhere else in the DOM, outside of Vue app. For example, due to styling requirements, we want to move <p id="content"> from it’s deeply nested position to the <div> with id="endofbody"

  1. <body>
  2. <div id="app" class="demo">
  3. <h3>Move the #content with the portal component</h3>
  4. <div>
  5. <p id="content">
  6. This should be moved to #endofbody.
  7. </p>
  8. <span>This content should be nested</span>
  9. </div>
  10. </div>
  11. <div id="endofbody"></div>
  12. </body>

To do so, we can use Vue’s built-in <teleport> component:

  1. <body>
  2. <div id="app" class="demo">
  3. <h3>Move the #content with the portal component</h3>
  4. <div>
  5. <teleport to="#endofbody">
  6. <p id="content">
  7. This should be moved to #endofbody.
  8. </p>
  9. </teleport>
  10. <span>This content should be nested</span>
  11. </div>
  12. </div>
  13. <div id="endofbody"></div>
  14. </body>

As a result, we will have teleport content moved in the rendered DOM tree:

See the Pen Teleport by Vue (@Vue) on CodePen.

As you can see, all of the children of <teleport> will be appended to <div id="endofbody">.

Using with Vue components

If <teleport> contains a Vue component, it will remain a logical child component of the <teleport>‘s parent:

  1. const app = Vue.createApp({
  2. template: `
  3. <h1>Root instance</h1>
  4. <parent-component />
  5. `
  6. })
  7. app.component('parent-component', {
  8. template: `
  9. <h2>This is a parent component</h2>
  10. <teleport to="#endofbody">
  11. <child-component name="John" />
  12. </teleport>
  13. `
  14. })
  15. app.component('child-component', {
  16. props: ['name'],
  17. template: `
  18. <div>Hello, {{ name }}</div>
  19. `
  20. })

In this case, even when child-component is rendered in the different place, it will remain a child of parent-component and will receive a name prop from it.

This also means that injections from a parent component work as expected, and that the child component will be nested below the parent component in the Vue Devtools, instead of being placed where the actual content moved to.

Using multiple teleports on the same target

A common use case scenario would be a reusable <Modal> component of which there might be multiple instances active at the same time. For this kind of scenario, multiple <teleport> components can mount their content to the same target element. The order will be a simple append - later mounts will be located after earlier ones within the target element.

  1. <teleport to="#modals">
  2. <div>A</div>
  3. </teleport>
  4. <teleport to="#modals">
  5. <div>B</div>
  6. </teleport>
  7. <!-- result-->
  8. <div id="modals">
  9. <div>A</div>
  10. <div>B</div>
  11. </div>

You can check <teleport> component options in the API reference