Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Chron's avatar
Level 6

How to listen Bootstrap 5 events in Vue 3?

I have this button that opens an offcanvas

<button type="button" class="btn-primary" data-bs-toggle="offcanvas" data-bs-target="#offcanvasLinks">
	Toggle Offcanvas
</button>

Bootstrap 5 drops jQuery so I can just use vanilla js to listen to BS events like show.bs.offcanvas. But is it recommended to do that inside Vue 3?

0 likes
1 reply
martinbean's avatar
Level 80

@chron You don’t need to manually add event listeners. Just import the Bootstrap JavaScript in the JavaScript file where you create your Vue application:

import 'bootstrap';
import { createApp } from 'vue';

const app = createApp({
    //
});

app.mount('#app');

This will include and initialise the Bootstrap JavaScript components, including offcanvas.

For things like modals, I do tend to wrap them in Vue components, and wrap the Bootstrap events in Vue component events:

<script setup>
import { defineEmits, defineProps, onMounted, ref, watch } from 'vue';
import { Modal } from 'bootstrap';

const props = defineProps({
  id: {
    required: true,
    type: String,
  },
  show: {
    default: false,
    required: false,
    type: Boolean,
  },
  title: {
    required: true,
    type: String,
  },
});

const emit = defineEmits(['hide', 'hidden', 'show', 'show']);

const bootstrapModal = ref(null);
const modalEl = ref(null);

onMounted(() => {
  if (modalEl.value) {
    bootstrapModal.value = Modal.getOrCreateInstance(modalEl.value);

    modalEl.addEventListener('hide.bs.modal', () => emit('hide'));
    modalEl.addEventListener('hidden.bs.modal', () => emit('hidden'));
    modalEl.addEventListener('show.bs.modal', () => emit('show'));
    modalEl.addEventListener('shown.bs.modal', () => emit('shown'));
  }
});

watch(() => props.show, (show) => {
  if (bootstrapModal.value) {
    show ? bootstrapModal.value.show() : bootstrapModal.value.hide();
  }
});
</script>

<template>
  <div
    aria-hidden="true"
    class="fade modal"
    ref="modalEl"
    tabindex="-1"
    v-bind:aria-labelledby="`${id}-label`"
    v-bind:id="id"
  >
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h1 class="modal-title fs-5" v-bind:id="`${id}-label`">{{ title }}</h1>
          <button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button>
        </div>
        <div class="modal-body">
          <slot name="body"></slot>
        </div>
        <div class="modal-footer" v-if="'footer' in $slots">
          <slot name="footer"></slot>
        </div>
      </div>
    </div>
  </div>
</template>

This means you can then show and hide the modal with a show prop, and also receive events when the modal is closed in order to update any props in the parent component:

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

const modalOpen = ref(false);
</script>

<template>
  <CustomModal v-bind:show="modalOpen" v-on:hide="modalOpen = false">
    <template v-slot:body>
      <!-- Modal body -->
    </template>
  </CustomModal>
</template>

You could probably re-factor this to a v-model directive if you really wanted to.

1 like

Please or to participate in this conversation.