useModal()
With useModal(), that means you don't have to add the modal to your Vue template and you don't have to use v-model or modalId to open or close the modal. You can simply use it to create a dynamic modal everywhere and control it programmatically.
Prerequisite
Dynamic modals are rendered by <ModalsContainer>, so make sure it is included in your app once:
<script setup lang="ts">
import { ModalsContainer } from 'vue-final-modal'
</script>
<template>
<!-- Your app -->
<ModalsContainer />
</template>
Usage
Passing Props and Events
useModal(options) takes the options that describe the modal component, and returns open() and close() functions to control it.
import { VueFinalModal, useModal } from 'vue-final-modal'
const { open, close } = useModal({
// Open the modal right after it was created, the default value is `false`.
defaultModelValue: false,
// Keep the modal mounted after it was closed, the default value is `false`.
keepAlive: false,
// The modal component. Use `<VueFinalModal>` directly or your own styled modal component.
component: VueFinalModal,
attrs: {
// Bind props to the modal component (VueFinalModal in this case).
clickToClose: true,
escToClose: true,
// Bind events to the modal component (VueFinalModal in this case).
// Any custom events can be listened for when prefixed with "on", e.g. "onEventName".
onBeforeOpen() { /* on before open */ },
onOpened() { /* on opened */ },
onBeforeClose() { /* on before close */ },
onClosed() { /* on closed */ },
},
})
// Open the modal
open().then(() => { /* Do something after modal opened */ })
// Close the modal
close().then(() => { /* Do something after modal closed */ })
Reactive attrs
attrs can also be a ref, computed or a getter function. The modal will keep in sync with it, so you can update the modal's props dynamically without any extra API:
import { ref } from 'vue'
import { VueFinalModal, useModal } from 'vue-final-modal'
const title = ref('Hello World!')
const { open, close } = useModal({
component: VueFinalModal,
attrs: () => ({
title: title.value,
}),
})
// Updating the ref updates the opened modal as well
title.value = 'Hello Vue!'
Keeping the modal alive
By default the modal is unmounted when it closes and mounted again by the next open(). With keepAlive: true it stays mounted after close(), hidden with display: none, so its state survives. destroy() unmounts it when you are done with it. A kept-alive modal is not destroyed when the component that created it unmounts, so call destroy() yourself. See the Modal Keep Alive example.
import { useModal } from 'vue-final-modal'
import ModalDraft from './ModalDraft.vue'
const { open, close, destroy } = useModal({
keepAlive: true,
component: ModalDraft,
})
await open()
await close() // Still mounted, the draft inside is kept
await open() // The draft is still there
destroy() // Unmounted, the next open() starts from a fresh component
Passing Slots
with String
import { VueFinalModal, useModal } from 'vue-final-modal'
const { open, close } = useModal({
component: VueFinalModal,
attrs: { ... },
slots: {
default: '<p>The content of the modal</p>'
}
})
with Component
import { VueFinalModal, useModal } from 'vue-final-modal'
// ModalContent is the component you want to put into the modal content
import ModalContent from './ModalContent.vue'
const { open, close } = useModal({
component: VueFinalModal,
attrs: { ... },
slots: {
// You can import your own component as a slot and put it to `slots.default` without binding props and events.
default: ModalContent
}
})
with Component, Props and Events
import { VueFinalModal, defineTemplate, useModal } from 'vue-final-modal'
// ModalContent is the component you want to put into the modal content
import ModalContent from './ModalContent.vue'
const { open, close } = useModal({
component: VueFinalModal,
attrs: { ... },
slots: {
default: defineTemplate({
component: ModalContent,
attrs: {
// Bind ModalContent props
title: 'Hello world!'
// Bind ModalContent events
onConfirm() { }
}
})
}
})
defineTemplate() is a function that provides better DX for type checking. It just returns the same object you passed in. useModalSlot() from 4.x does the same and keeps working, and defineModal() is the same kind of helper for the template of a whole modal: its component, attrs and slots.What open() and close() resolve with
open() resolves with 'opened' once the enter transition has finished, and close() resolves with 'closed' once the leave transition has finished. They never stay pending: when the modal does not get there, they resolve with a message that says why, so compare the result to know what happened.
const { open } = useModal({ component: ModalConfirm })
if (await open() === 'opened') {
// The modal is opened and its enter transition has finished
}
They resolve with a message when:
- the modal is already opened, or already closed,
beforeOpenorbeforeClosecalledevent.stop(),- the modal was toggled again before it finished, for example closed by the
esckey while it was still opening, - the modal was destroyed before it finished, by
destroy()or because the component that created it unmounted.
A close() called while the modal is still opening waits until it has opened, then closes it: open() resolves with 'opened' and close() with 'closed'.
Server-side rendering
A modal opened while a component sets up, with defaultModelValue: true or an open() called in setup(), is part of the server HTML and hydrates already open. On the server its open() resolves with 'opened' right away.
A modal opened outside a component, for example from a Nuxt plugin or a route middleware, is skipped on the server with a warning, and its open() resolves right away there. In the browser it opens once the app has mounted. Do not await that open() before the app mounts: it settles once the modal has opened.
After an await in setup(), it depends on where the component sits. A component around <ModalsContainer>, such as app.vue, finishes its setup before the container renders, so its modal is still part of the server HTML, as long as it calls useModal() before the await or uses <script setup>. The server does not wait for a component next to the container, such as a page: its modal shows in the browser only, after hydration.