How to build a modal in Vue

Modal components provide overlay dialogs for user interactions without navigating away from the current page, essential for confirmations, forms, and focused content. As the creator of CoreUI, a widely used open-source UI library, I’ve built modal systems in Vue applications throughout my 11 years of Vue development. The most effective approach is using Vue 3’s Teleport component with Composition API to render modals at the document body level. This method prevents z-index conflicts and ensures modals display above all page content.

Use Teleport component to render modal at body level with conditional visibility control.

<template>
  <button @click="isOpen = true">Open Modal</button>

  <Teleport to="body">
    <div v-if="isOpen" class="modal-overlay" @click="isOpen = false">
      <div class="modal-content" @click.stop>
        <h2>Modal Title</h2>
        <p>This is modal content.</p>
        <button @click="isOpen = false">Close</button>
      </div>
    </div>
  </Teleport>
</template>

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

const isOpen = ref(false)
</script>

<style scoped>
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-content {
  background: white;
  padding: 2rem;
  border-radius: 8px;
  max-width: 500px;
}
</style>

Here Teleport renders the modal content directly to the document body, bypassing parent component stacking contexts. The v-if directive controls modal visibility based on isOpen state. The overlay div uses @click to close the modal when clicking outside, while @click.stop on modal-content prevents event propagation, keeping the modal open when clicking inside. The ref(false) creates reactive state for toggling modal visibility.

Best Practice Note:

This is the modal pattern we implement in CoreUI Vue components for reliable overlay behavior. Add keyboard event listeners for Escape key to close modals, implement focus trapping to keep tab navigation within modal bounds, and use CSS transitions with Vue’s Transition component for smooth open/close animations.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author