Setup
Play online
You can start playing with Vue Final Modal in your browser using our online sandboxes:
Installation
yarn add vue-final-modal vue-use-template @vueuse/core @vueuse/integrations focus-trap
npm install vue-final-modal
pnpm add vue-final-modal
Vue Final Modal needs Vue 3.3 or later. vue-use-template, @vueuse/core, @vueuse/integrations and focus-trap are peer dependencies: npm and pnpm install them for you, Yarn does not, so the Yarn command lists them.
From a CDN
The UMD build defines a global VueFinalModal and reads the globals of its peer dependencies, so load them first, in this order. vue-use-template and the scroll lock are bundled into it.
<link rel="stylesheet" href="https://unpkg.com/vue-final-modal@5/dist/style.css">
<div id="app">
<button @click="() => open()">Open Modal</button>
<modals-container></modals-container>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/@vueuse/shared@15/dist/index.iife.min.js"></script>
<script src="https://unpkg.com/@vueuse/core@15/dist/index.iife.min.js"></script>
<script src="https://unpkg.com/tabbable@6/dist/index.umd.min.js"></script>
<script src="https://unpkg.com/focus-trap@8/dist/focus-trap.umd.min.js"></script>
<script src="https://unpkg.com/@vueuse/integrations@15/dist/useFocusTrap.iife.min.js"></script>
<script src="https://unpkg.com/vue-final-modal@5"></script>
<script>
const { createApp } = Vue
const { ModalsContainer, createVfm, useModal } = VueFinalModal
createApp({
components: { ModalsContainer },
setup() {
const { open } = useModal({
slots: { default: '<p>The content of the modal</p>' },
})
return { open }
},
}).use(createVfm()).mount('#app')
</script>
VueUse 10 to 13 serve the same files from the package root, without dist/.
Plugin registration
Create the plugin with createVfm() and register it. Vue Final Modal keeps a shared context that is provided to every modal through provide/inject.
Vue 3
import { createApp } from 'vue'
import { createVfm } from 'vue-final-modal'
import App from './App.vue'
const app = createApp(App)
const vfm = createVfm()
app.use(vfm).mount('#app')
Nuxt
Install the Nuxt module. It registers the plugin and the required CSS for you, and auto-imports <VueFinalModal>, <ModalsContainer>, useModal(), useModalSlot(), useVfm(), useVfmAttrs(), defineModal() and defineTemplate():
export default defineNuxtConfig({
modules: ['vue-final-modal/nuxt'],
})
If another module auto-imports the same names, such as a useModal() of its own, turn the auto-imports off and import them from vue-final-modal yourself:
export default defineNuxtConfig({
modules: ['vue-final-modal/nuxt'],
vueFinalModal: {
autoImports: false,
},
})
Add <ModalsContainer> to your app
<ModalsContainer> renders the dynamic modals created by useModal(). You don't need to add anything else to it, and you only need to put it once in your app:
<script setup lang="ts">
import { ModalsContainer } from 'vue-final-modal'
</script>
<template>
<!-- Your app -->
<ModalsContainer />
</template>
In the component tree the modals are children of <ModalsContainer>, so their content can inject what its ancestors provide, not what the component calling useModal() provides. If your app wraps its content in a component that provides context, such as Nuxt UI's <UApp>, put <ModalsContainer> inside it:
<template>
<UApp>
<NuxtPage />
<ModalsContainer />
</UApp>
</template>
Import required CSS
Vue Final Modal has a tiny amount of required CSS (gzipped 0.49kb). All classes have a .vfm- prefix, so you don't have to worry about any CSS pollution.
Vue 3
import 'vue-final-modal/style.css'
Nuxt
The vue-final-modal/nuxt module adds the CSS for you. If you don't use the module, add it manually:
export default defineNuxtConfig({
css: ['vue-final-modal/style.css'],
})
Style a modal
Define a styled modal for yourself. Here will create a styled <ModalConfirm> with <VueFinalModal> as an example.
Plain CSS
Use plain CSS to define a <ModalConfirmPlainCss> component with <VueFinalModal>.
v-html. Never pass user-provided content this way, see Passing Slots with String.- Basic example
<script setup lang="ts"> import { useModal } from 'vue-final-modal' import ModalConfirmPlainCss from './ModalConfirmPlainCss.vue' const { open, close } = useModal({ component: ModalConfirmPlainCss, attrs: { title: 'Hello World!', onConfirm() { close() }, }, slots: { default: '<p>The content of the modal</p>', }, }) </script> <template> <VButton @click="() => open()"> Open Modal </VButton> </template><script setup lang="ts"> import { VueFinalModal } from 'vue-final-modal' defineProps<{ title?: string }>() const emit = defineEmits<{ (e: 'confirm'): void }>() </script> <template> <VueFinalModal class="confirm-modal" content-class="confirm-modal-content" overlay-transition="vfm-fade" content-transition="vfm-fade" > <h1>{{ title }}</h1> <slot /> <button @click="emit('confirm')"> Confirm </button> </VueFinalModal> </template> <style> .confirm-modal { display: flex; justify-content: center; align-items: center; } .confirm-modal-content { display: flex; flex-direction: column; padding: 1rem; background: #fff; border-radius: 0.5rem; } .confirm-modal-content > * + *{ margin: 0.5rem 0; } .confirm-modal-content h1 { font-size: 1.375rem; } .confirm-modal-content button { margin: 0.25rem 0 0 auto; padding: 0 8px; border: 1px solid; border-radius: 0.5rem; } .dark .confirm-modal-content { background: #000; } </style>
Atomic CSS (Recommended)
Let's take TailwindCSS for example to define a <ModalConfirm> component with <VueFinalModal>.
- Basic example
<script setup lang="ts"> import { useModal } from 'vue-final-modal' import ModalConfirm from './ModalConfirm.vue' const { open, close } = useModal({ component: ModalConfirm, attrs: { title: 'Hello World!', onConfirm() { close() }, }, slots: { default: '<p>UseModal: The content of the modal</p>', }, }) </script> <template> <VButton @click="() => open()"> Open Modal </VButton> </template><script setup lang="ts"> import { VueFinalModal } from 'vue-final-modal' defineProps<{ title?: string }>() const emit = defineEmits<{ (e: 'confirm'): void }>() </script> <template> <VueFinalModal class="flex justify-center items-center" content-class="flex flex-col max-w-xl mx-4 p-4 bg-white dark:bg-gray-900 border dark:border-gray-700 rounded-lg space-y-2" > <h1 class="text-xl"> {{ title }} </h1> <slot /> <button class="mt-1 ml-auto px-2 border rounded-lg" @click="emit('confirm')"> Confirm </button> </VueFinalModal> </template>
Control a modal
There are three ways to control a modal component.
Take the <ModalConfirm> component we built on previous section as an example:
useModal()
useModal() is a composable function that is used to create a dynamic modal, then you can control the modal programmatically with the returned open() and close() functions.
The dynamic modals are rendered by <ModalsContainer>, so make sure it is included in your app.
- Basic examplePreview.vue
<script setup lang="ts"> import { useModal } from 'vue-final-modal' import ModalConfirm from './ModalConfirm.vue' const { open, close } = useModal({ component: ModalConfirm, attrs: { title: 'Hello World!', onConfirm() { close() }, }, slots: { default: '<p>UseModal: The content of the modal</p>', }, }) </script> <template> <VButton @click="() => open()"> Open Modal </VButton> </template>
Read more in API > Composables > Use Modal.
v-model
Use v-model for show/hide a modal.
- Basic examplePreview.vue
<script setup lang="ts"> import { ref } from 'vue' import ModalConfirm from './ModalConfirm.vue' const show = ref(false) function confirm() { show.value = false } </script> <template> <VButton @click="show = true"> Open Modal </VButton> <ModalConfirm v-model="show" title="Hello World!" @confirm="() => confirm()" > <p>VModel: The content of the modal</p> </ModalConfirm> </template>
modalId
modelValue is not a required prop for <VueFinalModal>. Without using v-model, you can also use useVfm() composable function to control the modal by given a modalId.
- Basic examplePreview.vue
<script setup lang="ts"> import { useVfm } from 'vue-final-modal' import ModalConfirm from './ModalConfirm.vue' const vfm = useVfm() const modalId = Symbol('modalId') function confirm() { vfm.close(modalId) } </script> <template> <VButton @click="() => vfm.open(modalId)"> Open Modal </VButton> <ModalConfirm :modal-id="modalId" title="Hello World!" @confirm="() => confirm()" > <p>The content of the modal</p> </ModalConfirm> </template>
Read more in API > Components > Vue Final Modal.