Vue Final Modal
Components

<VueFinalModal>

Template structure

This is the simplify template of the <VueFinalModal>. It will be helpful for understanding the naming of props.

VueFinalModal.vue
<template>
  <Teleport :to="teleportTo">
    <div class="vfm" role="dialog" aria-modal="true">
      <Transition v-bind="overlayTransition">
        <div class="vfm__overlay"></div>
      </Transition>
      <Transition v-bind="contentTransition">
        <div class="vfm__content">
          <slot />
          <div class="vfm-swipe-banner-container"></div>
        </div>
      </Transition>
    </div>
  </Teleport>
</template>

Props

teleportTo

  • Description:
    • Set null | false to disable teleport.
    • The default is 'body' because that is the target Nuxt renders on the server, see Nuxt teleports.
  • Type:
    • JS: [String, null, Boolean, Object]
    • TS: PropType<string | RendererElement | null | false>
  • Default: 'body'

modalId (optional)

modalId should be an uniq value for the operation of a modal via useVfm().

  • Type:
    • JS: [String, Number, Symbol]
    • TS: PropType<ModalId>
      export type ModalId = number | string | symbol
      
      import { ModalId } from 'vue-final-modal'
      
  • Default: undefined

modelValue (optional)

Display the modal or not.

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: undefined

displayDirective

Render the modal via 'if', 'show' or 'visible' directive. If you need to keep the modal alive, you can use 'show' or 'visible' directive. for example if you have a draggable and resizable modal and need to keep the size and position of the modal while re-open it (see example).

  • Type:
    • JS: 'if' | 'show' | 'visible'
    • TS: PropType<'if' | 'show' | 'visible'>
  • Default: 'if'

hideOverlay

Hide the overlay or not.

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: undefined

overlayBehavior

Whether the overlay stays visible when another modal opens on top of this one.

  • Type:
    • JS: String
    • TS: PropType<'auto' | 'persist'>
  • Default: auto
    • By default, the value of overlayBehavior will be auto. This means that when you open multiple modals, only the overlay of the top modal will be displayed. On the other hand, if you set the modal's overlayBehavior to persist, the modal's overlay will always be visible, allowing you to have multiple overlays at the same time.

overlayTransition

This prop allow you to setup the transition for .vfm__overlay element. You can pass a transition name or use the built-in transition name provide by vue-final-modal including 'vfm-fade', 'vfm-slide-down', 'vfm-slide-up', 'vfm-slide-right', 'vfm-slide-left'. You can also pass the TransitionProps to customize your own transition.

  • Type:
    • JS: [String, Object]
    • TS: PropType<string | 'vfm-fade' | 'vfm-slide-down' | 'vfm-slide-up' | 'vfm-slide-right' | 'vfm-slide-left' | TransitionProps>
  • Default: undefined

contentTransition

This prop allow you to setup the transition for .vfm__content element. You can pass a transition name or use the built-in transition name provide by vue-final-modal including 'vfm-fade', 'vfm-slide-down', 'vfm-slide-up', 'vfm-slide-right', 'vfm-slide-left'. You can also pass the TransitionProps to customize your own transition.

  • Type:
    • JS: [String, Object]
    • TS: PropType<string | 'vfm-fade' | 'vfm-slide-down' | 'vfm-slide-up' | 'vfm-slide-right' | 'vfm-slide-left' | TransitionProps>
  • Default: undefined

overlayClass

Bind class to .vfm__overlay element.

  • Type:
    • JS: [Object, Array, String]
    • TS: [Object, Array, String] as unknown as PropType<any>
  • Default: undefined

contentClass

Bind class to .vfm__content element.

  • Type:
    • JS: [Object, Array, String]
    • TS: [Object, Array, String] as unknown as PropType<any>
  • Default: undefined

overlayStyle

Bind style to .vfm__overlay element.

  • Type:
    • JS: [String, Object, Array]
    • TS: PropType<StyleValue>
      export type StyleValue = string | CSSProperties | (string | CSSProperties)[]
      
      import { StyleValue } from 'vue-final-modal'
      
  • Default: undefined

contentStyle

Bind style to .vfm__content element.

  • Type:
    • JS: [String, Object, Array]
    • TS: PropType<StyleValue>
      export type StyleValue = string | CSSProperties | (string | CSSProperties)[]
      
      import { StyleValue } from 'vue-final-modal'
      
  • Default: undefined

clickToClose

Can be closed by clicking the .vfm element.

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: true

escToClose

Can be closed by keypress esc.

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: true

background

Is it allow to interact with background of the modal.

  • Type:
    • JS: 'interactive' | 'non-interactive'
    • TS: PropType<'interactive' | 'non-interactive'>
  • Default: 'non-interactive'

focusTrap

  • Description:
    • Set false to disable the focusTrap.
    • For more information on what options can be passed, see createOptions in the focus-trap documentation.
    • The options you pass are merged over { allowOutsideClick: true, escapeDeactivates: false }: outside clicks stay allowed so clickToClose and an interactive background keep working, and the esc key is handled by escToClose.
  • Type:
    • JS: [Boolean, Object]
    • TS: PropType<false | Options>
  • Default: () => ({})

lockScroll

Lock the scroll of the page while the modal is opened. A modal rendered inside a scroll container, for example with teleportTo set to false, locks that container too. Locks are counted, so the scroll comes back when the last modal holding it closes. On iOS, touch scrolling keeps working inside the scrollable elements of the modal.

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: true

reserveScrollBarGap

Keep the room of the scrollbar that disappears when the scroll is locked, so the content does not shift. The page gets the scrollbar's width as padding-right. A locked scroll container keeps a stable scrollbar-gutter, or gets the width as padding on its scrollbar's side where scrollbar-gutter is not supported.

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: true

zIndexFn

Define how to increase the zIndex when there are nested modals.

  • Type:
    • JS: Function
    • TS: PropType<(context: { index: number }) => number | undefined>
  • Default: ({ index }: { index: number }) => 1000 + 2 * index
    • index is the index order of the opened modals

swipeToClose

The direction of swiping to close the modal

  • Type:
    • JS: 'none' | 'up' | 'right' | 'down' | 'left'
    • TS: PropType<'none' | 'up' | 'right' | 'down' | 'left'>
  • Default: 'none'

threshold

Threshold for swipe to close

  • Type:
    • JS: Number
    • TS: PropType<number>
  • Default: 0

showSwipeBanner

If set :showSwipeBanner="true", only allow clicking swipe-banner slot to swipe to close

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: undefined

preventNavigationGestures

When set :preventNavigationGestures="true", there will be two invisible bars for prevent navigation gestures including swiping back/forward on mobile webkit. For example: Safari mobile.

  • Type:
    • JS: Boolean
    • TS: PropType<boolean>
  • Default: undefined

Events

  • (e: 'beforeOpen', event: { stop: () => void }): void: Emits while modal is still invisible, but before transition starting.
    • event.stop() is use to prevent the modal from opening. The pending open() then resolves with a message instead of 'opened'.
  • (e: 'opened'): void: Emits after modal became visible and transition ended.
  • (e: 'beforeClose', event: { stop: () => void }): void: Emits before modal is going to be closed.
    • event.stop() is use to prevent the modal from closing. The pending close() then resolves with a message instead of 'closed'.
  • (e: 'closed'): void: Emits after modal became invisible and transition ended. A modal opened by useModal() is unmounted right after, unless it has keepAlive.
  • (e: 'clickOutside'): void: Emits while .vfm element on click.

Slots

  • The default slot can be used to render the modal content.

You can also use scoped slots to close modal, for example:

<VueFinalModal>
  <template #default="{ close }">
    <button @click="() => close()">
      Cancel
    </button>
  </template>
</VueFinalModal>
  • The swipe-banner slot can be used to define the area that can be swipe to close

Wrapping <VueFinalModal>

To build your own modal component that still takes every prop of <VueFinalModal>, spread vueFinalModalProps into its props and bind the result of useVfmAttrs() to the inner <VueFinalModal>:

MyModal.vue
<script setup lang="ts">
import type { VueFinalModalEmits } from 'vue-final-modal'
import { VueFinalModal, useVfmAttrs, vueFinalModalProps } from 'vue-final-modal'

const props = defineProps({
  ...vueFinalModalProps,
  title: { type: String, default: '' },
})
const emit = defineEmits<VueFinalModalEmits>()
defineOptions({ inheritAttrs: false })

const vfmAttrs = useVfmAttrs({ props, modalProps: vueFinalModalProps, emit })
</script>

<template>
  <VueFinalModal v-bind="vfmAttrs">
    <h1>{{ title }}</h1>
    <slot />
  </VueFinalModal>
</template>

useVfmAttrs() passes the modal props on, forwards the modal events through emit and lets every other attr fall through to <VueFinalModal>, including the listeners useModal() uses to know when the modal has opened or closed.

Copyright © 2026