Vue Final Modal
Guide

Setup

How to start with Vue Final Modal, by creating a fresh new project or adding it to your Vue application.
❗️ If you came from vue-final-modal 4.x, please check the migration guide.

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

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.

index.html
<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

main.ts
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():

nuxt.config.ts
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:

nuxt.config.ts
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:

App.vue
<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:

app.vue
<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

main.ts
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:

nuxt.config.ts
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>.

The examples below pass the modal content as a string slot, which is rendered as raw HTML like 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>
    
Highly recommended to use TailwindCSS or UnoCSS to define your modals.

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>
    

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 example
    Preview.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 example
    Preview.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 example
    Preview.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.

Copyright © 2026