Depending on the loading state, sometimes the <Teleport />
's target container will be mounted later than the content's mounting, causing Vue error
[Vue warn]: Invalid Teleport target on mount: null
.
How do I ensure that Vue only attempts to teleport after the container has been mounted or only teleport if the container is present?
First, we create a way to track if our targeted container has been mounted. We want this to be globally accessible so that components from other parts of the rendering tree can access the state.
// useTeleportTarget.ts
const teleportMap: Record<string, Ref<HTMLElement | undefined>> = {}
export function useTeleportTarget(elementId: string) {
if (!teleportMap[elementId])
teleportMap[elementId] = ref<HTMLElement>()
return teleportMap[elementId]
}
Then we create an easy-to-use Teleport component that wraps over the original API. This component helps us detect if the container has been mounted before attempting to teleport.
// BetterTeleport.vue
<script setup lang="ts">
import { useTeleportTarget } from '@/helpers/useTeleportTarget'
const props = defineProps<{
to: string
}>()
const target = useTeleportTarget(props.to)
</script>
<template>
<Teleport v-if="target" :to="`#${to}`">
<slot />
</Teleport>
</template>
Using the new setup:
// Container.vue
<script setup>
// We register our container target with the element ID
const myContainer = useTeleportTarget('my-container')
// Not important, only to demonstrate this container may or may not be mounted
const showTeleportContainer = ref(true)
</script>
<template>
<div
v-if="showTeleportContainer"
id="my-container"
ref="myContainer"
></div>
</template>
With this, teleporting should always work!
// FarAwayContent.vue
<template>
<BetterTeleport to="my-container">
I love my swamp!
</BetterTeleport>
</template>