To download this project, clone it from my Github repo.

I am using Vue JS 3, and I found that the documentation of Bootstrap could not help me use modals in my Vue.js project. The problem is that the documentation at Bootstrap 5’s site is cryptic about toggling the modal without using a button. It talks of using:

var myModal = new bootstrap.Modal(document.getElementById('myModal'), options)

But I couldnt import bootstrap into my project. Perhaps this was because I was using the CDN version instead of self hosting it.

It’s easy to implement this once you understand how Bootstrap modals work. Bootstrap modals have a div element with a class of modal fade. When it is triggered, this element gets the show attribute (not a class), and d-block class as well. In addition, the body tag gets an additional class of modal-open. When the modal is closed, this process is reversed. Understanding this, we can easily implement Bootstrap 5 modals in one’s code:

Import Bootstrap 5’s CDN in your code. Add both the CSS and JS to your code.

Our sample Single Page Component will look like this:

Template:

<template>
<div>  
    <p>Test modal<a href="#" @click="modalToggle">now</a></p>
    <div>
        <button type="button" class="btn btn-primary" @click="modalToggle">My Modal</button>
            <div
            ref="modal"
            class="modal fade"
            :class="{ show: active, 'd-block': active }"
            tabindex="-1"
            role="dialog">
            <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                <h5 class="modal-title">Modal title</h5>
                <button
                    type="button"
                    class="close"
                    data-dismiss="modal"
                    aria-label="Close"
                    @click="modalToggle">
                    <span aria-hidden="true">&times;</span>
                </button>
                </div>
                <div class="modal-body">
                <p>Modal body text goes here.</p>
                </div>
            </div>
            </div>
        </div>
        <div v-if="active" class="modal-backdrop fade show"></div>
        </div>
</div>
</template>

Script:

<script>
export default {
data() {
    return {
    active: false,
    }
},
methods: {
    modalToggle() {
    const body = document.querySelector("body")
    this.active = !this.active
    this.active ? body.classList.add("modal-open") : body.classList.remove("modal-open")
    },
},
}
</script>

Here, we have a variable active which is initially set false. So modal will not show up on page load. On clicking a link, we use a method to toggle this variable. This will remove the show attribute and the d-block class from our modalm and remove the modal-open property from the body tag.

Using Bootstrap modals when the modal is implemented as a Child Component.

Let’s assume we have a parent component, which will use the BootstrapModal as a component. That’s a very common scenario.

Parent:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>

    <h3>Test Bootstrap Modal</h3>
    <bootstrap-modal :showModal="showModalNow" @closeModal="closeMyModal"></bootstrap-modal>
    <a href="#" @click="toggleModal">now</a>
  </div>
</template>

So, we are adding the modal as a component. It will be turned on by setting our variable showModalNow to true. It can be closed by clicking the close button on the opened modal.

<script>
import BootstrapModal from "./BootstrapModal.vue"
export default {
  components: {
    "bootstrap-modal": BootstrapModal,
  },
  data() {
    return {
      showModalNow: false,
    }
  },
  methods: {
    closeMyModal() {
      this.showModalNow = false;
    },
    toggleModal() {
      this.showModalNow = !this.showModalNow;
    }
  }
}
</script>

Now the child component

<template>
  <teleport to="body">
    <div
      ref="modal"
      class="modal fade"
      :class="{ show: active, 'd-block': active }"
      tabindex="-1"
      role="dialog"
    >
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header bg-primary text-light">
            <h5 class="modal-title">Modal title</h5>
            <button
              type="button"
              class="close"
              data-dismiss="modal"
              aria-label="Close"
              v-on:click="$emit('closeModal')"
            >
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body bg-success text-dark">
            <p>Modal body text goes here.</p>
          </div>
          <div class="modal-footer bg-warning text-dark">
            <button type="button" class="btn btn-secondary" v-on:click="$emit('closeModal')">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
  </teleport>
</template>
<script>
export default {
  name: "BootstrapModal",
  emits: ["closeModal"],
  props: {
    showModal: Boolean,
  },
  watch: {
    showModal: {
      handler(newVal) {
        this.active = newVal;
        const body = document.querySelector("body");
        this.showModal ? body.classList.add("modal-open") : body.classList.remove("modal-open")
      },
      immediate: true,
      deep: true,
    },
  },
  data() {
    return {
      active: this.showModal,
    };
  },
};
</script>

References:

  1. Class list API on MDN Documents
  2. Stackoverflow post
  3. Bootstrap 5 modal reference