Vue Final Modal
Composables

useModalLifecycle()

Build your own modal component that useModal() can open and await.

useModal() renders any component you give it. It passes the component a modelValue, and settles open() and close() on the events the component emits once it has opened or closed. useModalLifecycle() emits them for you, so your component only shows and hides itself. <VueFinalDialog> is built on it.

Usage

Declare modalComponentProps and ModalComponentEmits, and give useModalLifecycle() an open and a close hook. A hook that returns a promise holds opened or closed back until it settles, for example until a transition ends:

MyDialog.vue
<script setup lang="ts">
import type { ModalComponentEmits } from 'vue-final-modal'
import { ref } from 'vue'
import { modalComponentProps, useModalLifecycle } from 'vue-final-modal'

const props = defineProps(modalComponentProps)
const emit = defineEmits<ModalComponentEmits>()

const dialog = ref<HTMLDialogElement>()
const { isOpen, reportClosed } = useModalLifecycle(props, emit, {
  open: () => dialog.value!.showModal(),
  close: () => dialog.value!.close(),
})

function onClose() {
  if (!dialog.value!.open)
    reportClosed()
}
</script>

<template>
  <dialog ref="dialog" @cancel.prevent="isOpen = false" @close="onClose">
    <slot />
  </dialog>
</template>
const { open } = useModal({ component: MyDialog })

await open() // 'opened'

The hooks run once the component has mounted and rendered the new state. The browser may close the dialog on Esc although cancel was prevented, which onClose reports. It skips the close event of a dialog opened again before the event fired.

Return value

  • isOpen: whether the modal is open. Set it to open or close the modal from inside, for example from a close button: beforeOpen or beforeClose is emitted first, and update:modelValue after.
  • reportClosed(): reports a modal that already closed by itself, such as a dialog a <form method="dialog"> closed, so beforeClose is not asked.
  • toggle(show?): opens or closes the modal and resolves once it has, like useVfm().toggle(). Called while the modal is heading there, it settles with that change.
  • status: a read-only ref of where the modal is, 'closed', 'opening', 'opened' or 'closing', like the status of useModal().

What it guarantees

Your component's open() and close() resolve with the same results as those of <VueFinalModal>:

  • 'opened' and 'closed' once the hook settled,
  • 'stopped' when beforeOpen or beforeClose called event.stop(), which also writes the previous value back to v-model,
  • 'toggled-again' when the modal was toggled again before its hook settled: the late hook does not emit opened or closed,
  • 'destroyed' when the component unmounted before its hook settled.

Events

ModalComponentEmits lists the events useModalLifecycle() emits: update:modelValue, beforeOpen, opened, beforeClose and closed, plus the internal _opened, _closed, _stopped and _status that useModal() listens to. In the Options API, use emits: modalComponentEmits.

Declare them only on the component that shows the modal. A wrapper around <VueFinalDialog> or <VueFinalModal> that declares them swallows the internal listeners of useModal(): let them fall through to the inner component instead.

Copyright © 2026