Show a Modal in Vue

Apr 20, 2020

A modal is a pop-up built entirely in JavaScript. Vue makes it easy to build a modal component using Vue slots. Click the below button to see a basic modal.

Building A Modal Component

The modal component can be broken down into 4 important elements: the mask, the container, the header, and the footer. Here's how these elements appear on the screen.

The mask is the grey background that partially hides the page, and the container is the white box that contains the header and footer. Below is the CSS for the above modal, slightly modified from this page.

.modal-mask {
  position: fixed;
  z-index: 9998;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: table;
  transition: opacity 0.3s ease;
}

.modal-wrapper {
  display: table-cell;
  vertical-align: middle;
}

.modal-container {
  width: 300px;
  margin: 0px auto;
  padding: 20px 30px;
  padding-bottom: 40px;
  background-color: #fff;
  border-radius: 2px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
  transition: all 0.3s ease;
}

.modal-default-button {
  float: right;
}

/*
 * The following styles are auto-applied to elements with
 * transition="modal" when their visibility is toggled
 * by Vue.js.
 *
 * You can easily play with the modal transition by editing
 * these styles.
 */

.modal-enter {
  opacity: 0;
}

.modal-leave-active {
  opacity: 0;
}

.modal-enter .modal-container,
.modal-leave-active .modal-container {
  -webkit-transform: scale(1.1);
  transform: scale(1.1);
}

The modal component is a standard Vue component with 2 named slots: header and footer. Below is the modal component definition.

Vue.component('modal', {
  template: `
  <transition name="modal">
    <div class="modal-mask">
      <div class="modal-wrapper">
        <div class="modal-container">
          <div class="modal-body">
            <slot name="body">
            </slot>
          </div>

          <div class="modal-footer">
            <slot name="footer">
              <button class="modal-default-button" @click="$emit('close')">
                OK
              </button>
            </slot>
          </div>
        </div>
      </div>
    </div>
  </transition>
  `
});

The modal component doesn't do much by itself. It just exposes 2 named slots. The footer slot has a default value that emits a 'close' event whenever the user clicks the 'OK' button.

Usage

How do you actually use this modal component? You need to conditionally render this component using v-if, because, if the modal component is rendered, the mask will hide the page. Below is the Vue app that powers this page's modal:

const app = new Vue({
  data: () => ({ showModal: false }),
  template: `
    <div>
      <button @click="showModal = true">Show Modal</button>
      <modal v-if="showModal" @close="showModal = false">
        <template v-slot:body>
          Hello, modal!
        </template>
      </modal>
    </div>
  `
});
app.$mount('#vue-modal-example');

The v-if directive tells Vue to only mount the modal if setModal is true. Clicking the button sets showModal = true, which tells Vue to mount the modal. The <template v-slot:body> tells Vue what HTML to render in the body slot. Since there's no footer slot, Vue uses the default footer HTML.

Finally, once the modal emits a 'close' event, this template is responsible for hiding the modal. The modal component is not allowed to close itself, the calling code is responsible for hiding it.


More Vue Tutorials