| |

Vue.js 44 🟢 Teleporting DOM Nodes with

A modal dialog has a layout problem. It needs to appear centered on the screen, above everything else, with a backdrop that covers the entire page. If the modal is rendered inside a deeply nested component, its parent’s CSS can interfere: an overflow: hidden, a transform, a z-index stacking context, or a position: relative ancestor. The modal’s markup is logically part of the component that opened it, but physically it needs to live somewhere else in the DOM. <Teleport> is the Vue 3 built-in that moves a component’s DOM output to a different location in the document while preserving the logical component hierarchy. The component tree is unchanged; only the rendered DOM is relocated.

Key point: <Teleport> moves DOM nodes, not component logic. Props, events, reactivity, and lifecycle hooks all behave as if the content were rendered in place. What changes is where the browser inserts the nodes in the document.


Why Teleport exists

The stacking-context problem. CSS stacking contexts are created by positioned elements with a z-index, by transform, by filter, and by several other properties. A modal rendered inside such an ancestor cannot escape its stacking context, no matter how high its z-index. It appears behind elements that are outside the ancestor. Teleporting the modal to body removes it from the ancestor’s stacking context and lets it sit above everything.

The overflow problem. An ancestor with overflow: hidden clips its children. A dropdown rendered inside such an ancestor is cut off at the ancestor’s edge. Teleporting it to body or to a dedicated container avoids the clipping.

The layout problem. An ancestor with a fixed height, a grid, or a flex context can constrain the size or position of a child. A tooltip or a popover rendered inside a table cell inherits the cell’s dimensions and cannot position itself freely. Teleporting it out of the cell gives it the full viewport to work with.

The accessibility problem. A modal should trap focus and be announced as a dialog by screen readers. Placing it at the top of the body is the conventional structure for a dialog, and assistive technologies expect it. Teleporting to a top-level container aligns the DOM with the accessibility expectations.

The logical-location problem. The component that opens the modal owns the modal’s state. It should not have to move its template to the top of the app just to get the right DOM position. Teleport lets the component keep its logical structure — the modal is a child in the component tree — while the DOM ends up elsewhere.

The SSR problem. With server-side rendering, teleported content must be hydrated correctly. Vue tracks teleported nodes through hydration, so the client knows which server-rendered DOM belongs to the teleported component. The to target must exist on the server as well as the client, or hydration will fail.


a. The Teleport component

<Teleport> has a required to prop and a disabled prop. The default slot contains the content to teleport. The content is rendered in the target location when to resolves to an element in the document.

The minimal usage

<script setup>
import { ref } from 'vue';

const open = ref(false);
</script>

<template>
  <button @click="open = true">Open modal</button>
  <Teleport to="body">
    <div v-if="open" class="modal">
      <p>Modal content</p>
      <button @click="open = false">Close</button>
    </div>
  </Teleport>
</template>

The modal markup is rendered as a direct child of <body> in the DOM. In the component tree, it is still a child of the component that contains the <Teleport>. The v-if is on the inner element, so the teleport itself is always present but the modal only renders when open is true.

The to prop

The to prop accepts a CSS selector string or an actual DOM element. The most common values are "body" and a dedicated container like "#modal-root".

<Teleport to="#modal-root">
  <div class="modal">...</div>
</Teleport>

If the target does not exist when the teleport mounts, Vue logs a warning and renders the content in place. The target must exist before the teleport renders.

The dedicated container

A dedicated container in index.html gives the teleport a stable target that is not the entire body.

<body>
  <div id="app"></div>
  <div id="modal-root"></div>
  <div id="tooltip-root"></div>
</body>

Two containers separate modals from tooltips, so their z-index layers and styling can be managed independently.

The disabled prop

The disabled prop turns the teleport off. The content is rendered in place instead of in the target.

<Teleport to="body" :disabled="isMobile">
  <div class="modal">...</div>
</Teleport>

On mobile, the modal renders inline; on desktop, it teleports to the body. The prop is reactive, so the behavior can change at runtime.

The multiple teleports to the same target

Multiple <Teleport> components can target the same location. Vue appends the content in the order the teleports render.

<Teleport to="#modal-root">
  <div>First</div>
</Teleport>
<Teleport to="#modal-root">
  <div>Second</div>
</Teleport>

The target receives both, in the order they appear in the template.

The Teleport and Transition

<Teleport> composes with <Transition>. The transition wraps the teleported content, so the enter and leave animations apply to the modal.

<Teleport to="body">
  <Transition name="fade">
    <div v-if="open" class="modal">...</div>
  </Transition>
</Teleport>

The transition is inside the teleport, so the animated element is the teleported content. The v-if is inside the transition, which is what triggers the animation.


b. Common patterns

Teleport is used in a handful of recurring patterns. Each one addresses a specific layout or stacking problem.

The modal dialog

The modal is the canonical use case. It is rendered at the top level, with a backdrop, and traps focus.

<script setup>
import { ref, watch } from 'vue';

const props = defineProps({ open: Boolean });
const emit = defineEmits(['close']);

watch(() => props.open, (isOpen) => {
  document.body.style.overflow = isOpen ? 'hidden' : '';
});
</script>

<template>
  <Teleport to="body">
    <Transition name="modal">
      <div v-if="props.open" class="modal-backdrop" @click.self="emit('close')">
        <div class="modal-content" role="dialog" aria-modal="true">
          <slot />
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

The backdrop covers the viewport. The click on the backdrop closes the modal. The content is announced as a dialog by assistive technologies.

The tooltip

A tooltip is positioned relative to the element it describes. Teleporting it to body avoids clipping by ancestors and lets the tooltip use fixed positioning.

<script setup>
import { ref, computed } from 'vue';

const trigger = ref(null);
const visible = ref(false);
const position = ref({ top: 0, left: 0 });

function show() {
  const rect = trigger.value.getBoundingClientRect();
  position.value = { top: rect.bottom + 8, left: rect.left };
  visible.value = true;
}
</script>

<template>
  <span ref="trigger" @mouseenter="show" @mouseleave="visible = false">
    <slot name="trigger" />
  </span>
  <Teleport to="body">
    <div
      v-if="visible"
      class="tooltip"
      :style="{ top: position.top + 'px', left: position.left + 'px' }"
    >
      <slot />
    </div>
  </Teleport>
</template>

The tooltip’s position is computed from the trigger’s bounding rectangle. The teleport lets the tooltip use fixed positioning without interference from ancestors.

The dropdown menu

A dropdown rendered inside a table or a scrollable container is clipped. Teleporting it to body lets it overflow freely.

<template>
  <div class="dropdown">
    <button @click="open = !open">Menu</button>
    <Teleport to="body">
      <ul v-if="open" class="dropdown-menu" :style="position">
        <li v-for="item in items" :key="item.id">{{ item.label }}</li>
      </ul>
    </Teleport>
  </div>
</template>

The dropdown’s position is computed from the trigger. The menu is not clipped by the table cell.

The notification system

Notifications appear at a fixed corner of the screen, independent of the component that triggered them. A global notification container at the top of the body is the target.

<Teleport to="#notification-root">
  <TransitionGroup name="notification">
    <div v-for="n in notifications" :key="n.id" class="notification">
      {{ n.message }}
    </div>
  </TransitionGroup>
</Teleport>

The notification component teleports to a dedicated container. The container is positioned with position: fixed and a high z-index.

The nested modal

A modal that opens another modal teleports both to the same container. The second modal renders after the first, so it appears on top.

<Teleport to="#modal-root">
  <div class="modal">...</div>
</Teleport>

The z-index is managed by the order of the teleports. The dedicated container can use CSS to layer the modals correctly.

The mobile fallback

On mobile, a dropdown may be better rendered inline. The disabled prop switches the behavior based on a media query.

<Teleport to="body" :disabled="isMobile">
  <div class="dropdown">...</div>
</Teleport>

The isMobile flag is computed from window.matchMedia or a reactive viewport observer.


c. Teleport and the component tree

Teleport changes the DOM, not the component tree. This has consequences for refs, events, provide/inject, and SSR.

The component hierarchy

The teleported content is still a child of the component that contains the <Teleport>. Its lifecycle hooks fire in the same order. Its props come from the same parent. Its events bubble to the same handler.

<Teleport to="body">
  <ChildComponent @click="handleClick" />
</Teleport>

The handleClick fires when the child is clicked, even though the child’s DOM is in body. The event bubbles through the Vue component tree, not the DOM tree.

The refs

A ref on a teleported element resolves to the DOM element in the target location.

<script setup>
import { ref } from 'vue';
const modalEl = ref(null);
</script>

<template>
  <Teleport to="body">
    <div ref="modalEl">...</div>
  </Teleport>
</template>

The modalEl.value is the actual DOM node, wherever it was teleported.

The provide and inject

provide and inject work across the teleport. The teleported content inherits the provides from its logical ancestors, not its DOM ancestors.

<script setup>
provide('theme', 'dark');
</script>

<template>
  <Teleport to="body">
    <Child />  <!-- injects 'theme' = 'dark' -->
  </Teleport>
</template>

The child injects the value from the logical ancestor, even though the DOM ancestor is body.

The event bubbling

Events emitted by the teleported component propagate through the Vue component tree, not the DOM tree. A listener on the parent receives the event normally.

<Teleport to="body">
  <button @click="$emit('close')">Close</button>
</Teleport>

The parent’s @close listener fires when the button is clicked. The DOM position does not affect the event propagation.

The CSS scope

Scoped styles apply to teleported content. The scope attribute is added to the elements regardless of where they end up in the DOM.

<style scoped>
.modal { position: fixed; }
</style>

The .modal class matches only the teleported modal, because the scope attribute is present on the element. Styles from the parent component leak into the modal only through the cascade, not through the scope.

The SSR considerations

During server-side rendering, the teleported content is rendered into the target location in the initial HTML. The client hydrates it in place. If the target element does not exist on the server, hydration fails.

<!-- Server-rendered HTML -->
<body>
  <div id="app">
    <!-- component content -->
  </div>
  <div id="modal-root">
    <div class="modal">...</div>  <!-- teleported during SSR -->
  </div>
</body>

The target must be present in the initial HTML. A target created dynamically on the client will not work with SSR.

The Teleport in devtools

Vue Devtools shows the teleported content under its logical parent, not the target. This is because the component tree is unchanged. The DOM inspector shows the content under the target.


Complete Example Session

<!-- ============================================
     PART 1: BASIC TELEPORT
     ============================================ -->

<script setup>
import { ref } from 'vue';

const open = ref(false);
</script>

<template>
  <button @click="open = true">Open</button>
  <Teleport to="body">
    <div v-if="open" class="modal">
      <p>Modal content</p>
      <button @click="open = false">Close</button>
    </div>
  </Teleport>
</template>

<!-- ============================================
     PART 2: DEDICATED CONTAINER
     ============================================ -->

<!-- index.html -->
<body>
  <div id="app"></div>
  <div id="modal-root"></div>
</body>

<!-- Modal.vue -->
<template>
  <Teleport to="#modal-root">
    <div class="modal">...</div>
  </Teleport>
</template>

<!-- ============================================
     PART 3: TRANSITION WITH TELEPORT
     ============================================ -->

<template>
  <Teleport to="body">
    <Transition name="fade">
      <div v-if="open" class="modal-backdrop">
        <div class="modal-content"><slot /></div>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
.fade-enter-active, .fade-leave-active { transition: opacity 0.2s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>

<!-- ============================================
     PART 4: MODAL COMPONENT
     ============================================ -->

<script setup>
import { watch } from 'vue';

const props = defineProps({ open: Boolean });
const emit = defineEmits(['close']);

watch(() => props.open, (isOpen) => {
  document.body.style.overflow = isOpen ? 'hidden' : '';
});
</script>

<template>
  <Teleport to="body">
    <Transition name="modal">
      <div v-if="props.open" class="modal-backdrop" @click.self="emit('close')">
        <div class="modal-content" role="dialog" aria-modal="true">
          <slot />
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<!-- ============================================
     PART 5: TOOLTIP COMPONENT
     ============================================ -->

<script setup>
import { ref } from 'vue';

const trigger = ref(null);
const visible = ref(false);
const position = ref({ top: 0, left: 0 });

function show() {
  const rect = trigger.value.getBoundingClientRect();
  position.value = { top: rect.bottom + 8, left: rect.left };
  visible.value = true;
}
</script>

<template>
  <span ref="trigger" @mouseenter="show" @mouseleave="visible = false">
    <slot name="trigger" />
  </span>
  <Teleport to="body">
    <div
      v-if="visible"
      class="tooltip"
      :style="{ top: position.top + 'px', left: position.left + 'px' }"
    >
      <slot />
    </div>
  </Teleport>
</template>

<!-- ============================================
     PART 6: DROPDOWN COMPONENT
     ============================================ -->

<script setup>
import { ref } from 'vue';

const open = ref(false);
const trigger = ref(null);
const position = ref({ top: 0, left: 0 });

function toggle() {
  if (!open.value) {
    const rect = trigger.value.getBoundingClientRect();
    position.value = { top: rect.bottom, left: rect.left };
  }
  open.value = !open.value;
}
</script>

<template>
  <div>
    <button ref="trigger" @click="toggle">Menu</button>
    <Teleport to="body">
      <ul
        v-if="open"
        class="dropdown-menu"
        :style="{ top: position.top + 'px', left: position.left + 'px' }"
      >
        <slot />
      </ul>
    </Teleport>
  </div>
</template>

<!-- ============================================
     PART 7: NOTIFICATION SYSTEM
     ============================================ -->

<!-- index.html -->
<div id="notification-root"></div>

<!-- NotificationContainer.vue -->
<script setup>
import { useNotifications } from '@/composables/useNotifications';
const { notifications } = useNotifications();
</script>

<template>
  <Teleport to="#notification-root">
    <TransitionGroup name="notification">
      <div
        v-for="n in notifications"
        :key="n.id"
        class="notification"
      >
        {{ n.message }}
      </div>
    </TransitionGroup>
  </Teleport>
</template>

<!-- ============================================
     PART 8: DISABLED TELEPORT
     ============================================ -->

<script setup>
import { computed } from 'vue';

const isMobile = computed(() =>
  window.matchMedia('(max-width: 768px)').matches
);
</script>

<template>
  <Teleport to="body" :disabled="isMobile">
    <div class="dropdown">...</div>
  </Teleport>
</template>

<!-- ============================================
     PART 9: REFS AND EVENTS ACROSS TELEPORT
     ============================================ -->

<script setup>
import { ref } from 'vue';

const modalEl = ref(null);
const emit = defineEmits(['close']);
</script>

<template>
  <Teleport to="body">
    <div ref="modalEl" class="modal">
      <button @click="emit('close')">Close</button>
    </div>
  </Teleport>
</template>

<!-- The parent's @close listener fires when the button is clicked.
     The modalEl ref points to the actual DOM element in body. -->

<!-- ============================================
     PART 10: SSR COMPATIBILITY
     ============================================ -->

<!-- index.html (server-rendered) -->
<body>
  <div id="app"><!-- server-rendered app --></div>
  <div id="modal-root"><!-- server-rendered modals --></div>
</body>

<!-- The target must exist in the initial HTML.
     A dynamically created target breaks hydration. -->

The ten parts showed basic teleport, a dedicated container, transition integration, the modal component, tooltip, dropdown, notification system, disabled teleport, refs and events, and SSR considerations.


Quick Reference

Teleport Props

PropTypePurpose
toString or ElementThe target location
disabledBooleanRender in place instead

Common Targets

TargetUse Case
bodyModals, tooltips, dropdowns
#modal-rootDedicated modal container
#tooltip-rootDedicated tooltip container
#notification-rootNotification system

Teleport Behavior

AspectBehavior
Component treeUnchanged
DOM positionMoved to target
RefsPoint to the DOM node in the target
EventsBubble through the component tree
Provide/injectFollow the component tree
Scoped stylesApply to the teleported content
LifecycleFires in the same order

Teleport and Other Components

ComponentComposition
<Transition>Wrap the teleported content
<TransitionGroup>For lists of teleported items
<Suspense>Teleport inside async components
<KeepAlive>Teleport preserves the cached state

Best Practices

✅ Do This:

<!-- Use a dedicated container. -->
<Teleport to="#modal-root">  <!-- ✅ -->

<!-- Put the v-if inside the teleport. -->
<Teleport to="body"><div v-if="open">...</div></Teleport>  <!-- ✅ -->

<!-- Wrap with Transition for enter/leave. -->
<Teleport to="body"><Transition name="fade"><div v-if="open" /></Transition></Teleport>  <!-- ✅ -->

<!-- Hide body scroll when a modal is open. -->
document.body.style.overflow = 'hidden';  <!-- ✅ -->

<!-- Use the disabled prop for responsive behavior. -->
<Teleport to="body" :disabled="isMobile">  <!-- ✅ -->

<!-- Ensure the target exists in SSR. -->
<div id="modal-root"></div>  <!-- ✅ -->

❌ Don’t Do This:

<!-- Don't teleport to a non-existent target. -->
<Teleport to="#does-not-exist">  <!-- ❌ Warning, renders in place -->

<!-- Don't put the v-if on the Teleport itself. -->
<Teleport v-if="open" to="body">  <!-- ❌ Teleport remounts -->

<!-- Don't expect DOM event bubbling to work. -->
<!-- Events bubble through the component tree, not the DOM.  -->  <!-- ❌ -->

<!-- Don't create the target dynamically on the client. -->
document.createElement('div');  // ❌ Breaks SSR hydration

<!-- Don't use Teleport for layout-only concerns. -->
<!-- CSS can handle many cases without moving the DOM.  -->  <!-- ❌ -->

Common Pitfalls

PitfallWhy It HappensFix
Target not foundElement does not exist at mountEnsure the target is in the initial HTML
Modal remountsv-if on the Teleport itselfPut v-if on the inner element
Transition does not fireTransition outside the teleportMove the transition inside
SSR hydration mismatchTarget created on the clientRender the target server-side
Event not receivedListener on the DOM parentListen through the component tree
Refs are nullRef on the Teleport, not the elementRef the inner element
Styles do not applyStyles not scoped to the teleported elementUse the same component’s scoped styles

Real-World Examples

1. Basic Modal

<Teleport to="body"><div v-if="open" class="modal">...</div></Teleport>

2. Dedicated Container

<Teleport to="#modal-root">...</Teleport>

3. Transition

<Teleport to="body"><Transition name="fade"><div v-if="open" /></Transition></Teleport>

4. Tooltip

<Teleport to="body"><div class="tooltip" :style="pos" /></Teleport>

5. Dropdown

<Teleport to="body"><ul v-if="open" class="dropdown" /></Teleport>

6. Notification

<Teleport to="#notification-root"><TransitionGroup>...</TransitionGroup></Teleport>

7. Disabled on Mobile

<Teleport to="body" :disabled="isMobile">...</Teleport>

8. Body Scroll Lock

watch(() => props.open, (v) => { document.body.style.overflow = v ? 'hidden' : ''; })

9. Ref to Teleported Element

<Teleport to="body"><div ref="modalEl">...</div></Teleport>

10. Nested Modals

<!-- Both teleport to #modal-root; the second appears on top. -->

Visual

Teleport: DOM Position vs Component Tree

┌────────────────────────────────────────────────────────────┐
│  COMPONENT TREE                  DOM TREE                  │
│                                                            │
│  App                             <body>                    │
│   │                               ├── <div id="app">       │
│   ├── Header                      │    ├── header          │
│   ├── Content                     │    ├── content         │
│   │    └── Modal ──────┐          │    │    └── button    │
│   └── Footer           │          │    └── footer          │
│                        │          │                        │
│                        │          └── <div id="modal-root">│
│                        │               └── modal           │
│                        │                        ▲          │
│                        └────────────────────────┘          │
│                                                            │
│  Modal is a child of Content in the component tree.        │
│  Modal is a child of #modal-root in the DOM tree.          │
│  Props, events, refs, and lifecycle follow the component   │
│  tree. Only the rendered DOM moves.                        │
└────────────────────────────────────────────────────────────┘

The Stacking Context Problem

┌────────────────────────────────────────────────────────────┐
│  STACKING CONTEXT                                          │
│                                                            │
│  WITHOUT TELEPORT                                          │
│  ┌──────────────────────────────────────────────────┐     │
│  │  <div class="container" style="transform: ...">   │     │
│  │    <div class="card">                             │     │
│  │      <div class="modal" style="z-index: 9999">    │     │
│  │        Modal appears behind other elements        │     │
│  │        because the ancestor creates a stacking    │     │
│  │        context.                                   │     │
│  │      </div>                                       │     │
│  │    </div>                                         │     │
│  │  </div>                                           │     │
│  │  <div class="other" style="z-index: 1">           │     │
│  │    This element is outside the context            │     │
│  │    and appears above the modal.                   │     │
│  │  </div>                                           │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  WITH TELEPORT                                             │
│  ┌──────────────────────────────────────────────────┐     │
│  │  <body>                                           │     │
│  │    <div class="container">...</div>               │     │
│  │    <div class="other">...</div>                   │     │
│  │    <div class="modal">                            │     │
│  │      The modal is a direct child of body.         │     │
│  │      No ancestor stacking context.                │     │
│  │      z-index works as expected.                   │     │
│  │    </div>                                         │     │
│  └──────────────────────────────────────────────────┘     │
└────────────────────────────────────────────────────────────┘

The Modal Structure

┌────────────────────────────────────────────────────────────┐
│  MODAL WITH TELEPORT                                       │
│                                                            │
│  <Teleport to="body">                                      │
│    <Transition name="modal">                               │
│      <div v-if="open" class="modal-backdrop">              │
│        <div class="modal-content" role="dialog"            │
│             aria-modal="true">                             │
│          <slot />                                          │
│        </div>                                              │
│      </div>                                                │
│    </Transition>                                           │
│  </Teleport>                                               │
│                                                            │
│  RENDERED IN BODY:                                         │
│  ┌──────────────────────────────────────────────────┐     │
│  │  <div class="modal-backdrop">                     │     │
│  │    ← covers the entire viewport                   │     │
│  │    ← @click.self closes                           │     │
│  │                                                   │     │
│  │    <div class="modal-content">                    │     │
│  │      ← centered on the viewport                   │     │
│  │      ← role="dialog"                              │     │
│  │      ← aria-modal="true"                          │     │
│  │      ← focus trap here                            │     │
│  │    </div>                                         │     │
│  │  </div>                                           │     │
│  └──────────────────────────────────────────────────┘     │
└────────────────────────────────────────────────────────────┘

Teleport and the Component Tree

┌────────────────────────────────────────────────────────────┐
│  WHAT FOLLOWS THE COMPONENT TREE                           │
│                                                            │
│  ┌──────────────────────────────────────────────────┐     │
│  │  PROPS             Follow the component tree      │     │
│  │  EVENTS            Bubble through component tree  │     │
│  │  REFS              Point to the DOM element       │     │
│  │  PROVIDE/INJECT    Follow the component tree      │     │
│  │  LIFECYCLE         Fires in the same order        │     │
│  │  SCOPED STYLES     Apply to the teleported node   │     │
│  │  DEVTOOLS          Shows under the logical parent │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  ┌──────────────────────────────────────────────────┐     │
│  │  DOM POSITION      Only this changes              │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  The component tree is the source of truth.                │
│  Teleport only relocates the rendered nodes.               │
└────────────────────────────────────────────────────────────┘

Summary

ItemValue
<Teleport>Built-in component that moves DOM nodes
to propCSS selector or DOM element for the target
disabled propRenders in place instead
Default slotThe content to teleport
Component treeUnchanged by teleporting
RefsPoint to the DOM element in the target
EventsBubble through the component tree
Provide/injectFollow the component tree
SSRTarget must exist in the initial HTML
Common targetsbody, #modal-root, #notification-root

Key takeaways:

  • <Teleport> moves DOM nodes, not component logic. The component tree is unchanged. Props, events, refs, lifecycle, and provide/inject all behave as if the content were rendered in place. Only the browser’s DOM position changes.
  • The to prop specifies the target. A CSS selector like "body" or "#modal-root", or a DOM element reference. The target must exist when the teleport mounts.
  • Put the v-if inside the teleport. The teleport itself should always be present. The conditional rendering belongs on the inner element, so the teleport does not remount.
  • Transitions go inside the teleport. The <Transition> wraps the teleported content, so the enter and leave animations apply correctly.
  • A dedicated container is cleaner than body. A <div id="modal-root"> at the top of the document gives the teleport a stable target and keeps modal content separate from the app.
  • The disabled prop switches behavior. On mobile, rendering inline may be preferable. The prop is reactive, so the behavior can change at runtime.
  • Events bubble through the component tree, not the DOM. A listener on the logical parent receives the event, even though the DOM node is elsewhere.
  • SSR requires the target to exist server-side. The target must be in the initial HTML. A target created dynamically on the client breaks hydration.
  • The component tree remains the source of truth. Devtools shows teleported content under its logical parent. The DOM inspector shows it under the target. Both are correct views of the same component.

Remember: <Teleport> solves the problem of rendering content in a different DOM location while keeping its logical component context. It is the tool for modals, tooltips, dropdowns, and notifications — components whose markup needs to live outside their parent’s DOM subtree but whose logic belongs inside it. The to prop selects the target, the disabled prop turns the behavior off, and the component tree remains unchanged. Props, events, refs, provide/inject, and lifecycle all follow the component tree. Only the DOM position moves. The result is a component that keeps its logical structure and its state management, while the browser renders its content where the layout needs it.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!