JAVASCRIPT
Building Reusable Overlay Components with Vue 3 Teleport
Discover how to create and manage reusable modal or overlay components that render outside the component's DOM hierarchy using Vue 3's Teleport feature.
// src/components/ModalOverlay.vue
<template>
<teleport to="body">
<div v-if="isOpen" class="modal-backdrop" @click.self="closeModal">
<div class="modal-content">
<button class="modal-close" @click="closeModal">×</button>
<slot></slot>
</div>
</div>
</teleport>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
isOpen: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:isOpen']);
const closeModal = () => {
emit('update:isOpen', false);
};
</script>
<style scoped>
.modal-backdrop {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
min-width: 300px;
max-width: 80%;
position: relative;
}
.modal-close {
position: absolute;
top: 10px;
right: 10px;
border: none;
background: transparent;
font-size: 1.5em;
cursor: pointer;
}
</style>
// src/App.vue (example usage)
<template>
<div>
<h1>My App</h1>
<button @click="showModal = true">Open Modal</button>
<ModalOverlay v-model:is-open="showModal">
<h2>Modal Title</h2>
<p>This content is inside the modal and can be anything!</p>
<button @click="showModal = false">Close from inside</button>
</ModalOverlay>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ModalOverlay from './components/ModalOverlay.vue';
const showModal = ref(false);
</script>
How it works: This snippet demonstrates creating a reusable modal component using Vue 3's `Teleport` feature. The `ModalOverlay.vue` component uses `<teleport to="body">` to render its content directly under the `body` tag, regardless of where the component is used in the component tree. This is ideal for modals, notifications, or tooltips to avoid styling and stacking context issues with parent elements. It also uses `v-model` for easy toggling of its visibility from the parent component (`App.vue`), making it highly reusable and controlled.