The Problem

Vue's <Teleport> component renders its content at a different location in the DOM. If the target element does not exist when the Teleport component mounts, you get a warning and the content renders inline instead.

Symptoms

  • Console warning: "Failed to locate Teleport target"
  • Modal or overlay renders in the wrong place
  • CSS styles for the target container do not apply
  • Teleported content appears inside the component instead of the body

Real Error Message

bash
[Vue warn]: Failed to locate Teleport target with selector "#modal-root"
Note: the target element must exist before the Teleport component is mounted.

Common Causes

Cause 1: Target Element Not in index.html

vue
<template>
  <Teleport to="#modal-root">
    <Modal :open="isOpen">Content</Modal>
  </Teleport>
</template>

If #modal-root does not exist in index.html, the Teleport fails.

Cause 2: Target Created by Another Component That Has Not Mounted

vue
<!-- App.vue -->
<template>
  <Sidebar />     <!-- Creates #sidebar-target -->
  <ModalContainer /> <!-- Teleports to #sidebar-target -->
</template>

If ModalContainer mounts before Sidebar, the target does not exist yet.

How to Fix It

Fix 1: Add Target to index.html

html
<!-- index.html -->
<body>
  <div id="app"></div>
  <div id="modal-root"></div>  <!-- Must exist before Vue mounts -->
</body>

Fix 2: Use body as Fallback Target

vue
<Teleport to="body">
  <Modal :open="isOpen">Content</Modal>
</Teleport>

The body element always exists.

Fix 3: Dynamic Target with Validation

```vue <script setup> const target = ref('#modal-root');

onMounted(() => { if (!document.querySelector(target.value)) { console.warn('Teleport target not found, falling back to body'); target.value = 'body'; } }); </script>

<template> <Teleport :to="target"> <Modal :open="isOpen">Content</Modal> </Teleport> </template> ```

Fix 4: Use v-if to Wait for Target

```vue <script setup> const targetReady = ref(false);

onMounted(() => { targetReady.value = !!document.querySelector('#modal-root'); }); </script>

<template> <Teleport v-if="targetReady" to="#modal-root"> <Modal :open="isOpen">Content</Modal> </Teleport> <Modal v-else :open="isOpen">Content</Modal> <!-- Fallback inline render --> </template> ```