Vue Final Modal
Use Cases

Modal Long Scroll

A modal whose content is longer than the screen, and scrolls with the page behind it locked.

A modal whose content is longer than the screen, like Bootstrap's scrolling long content modal.

Preview

Preview.vue
  <script setup lang="ts">
  import { useModal } from 'vue-final-modal'
  import ModalLongScroll from './ModalLongScroll.vue'

  const p = `<p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras ullamcorper,
        ex at blandit iaculis, nibh urna lacinia tellus, a auctor lorem libero a
        lectus. Nulla bibendum mollis sapien, nec euismod mi mattis cursus.
        Curabitur accumsan erat eu viverra fringilla. Interdum et malesuada fames
        ac ante ipsum primis in faucibus. Phasellus ut neque eu diam vehicula
        efficitur. Vivamus ultricies purus facilisis augue porttitor vestibulum a
        non neque. Nunc egestas risus elit, sed fringilla velit posuere et.
      </p>`

  const { open, close } = useModal({
    component: ModalLongScroll,
    attrs: {
      title: 'Hello World!',
      onConfirm: () => close(),
    },
    slots: {
      default: p + p + p + p + p + p + p + p + p + p + p + p,
    },
  })
  </script>

  <template>
    <VButton @click="open">
      Open Modal
    </VButton>
  </template>

<ModalLongScroll> component

  <script setup lang="ts">
  import { useId } from 'vue'
  import { VueFinalModal } from 'vue-final-modal'

  defineProps<{
    title?: string
  }>()

  const emit = defineEmits<{
    (e: 'update:modelValue', modelValue: boolean): void
    (e: 'confirm'): void
  }>()

  const titleId = useId()

  /** A drag that starts on the panel and ends on the backdrop must not close the modal. */
  let pressedOnBackdrop = false

  function onMousedown(e: MouseEvent) {
    pressedOnBackdrop = e.target === e.currentTarget
  }

  function onMouseup() {
    if (pressedOnBackdrop)
      emit('update:modelValue', false)
  }
  </script>

  <template>
    <VueFinalModal
      content-transition="vfm-fade"
      overlay-transition="vfm-fade"
      content-class="absolute inset-0"
      :aria-labelledby="titleId"
      @update:model-value="(val) => emit('update:modelValue', val)"
    >
      <div
        class="absolute inset-0 h-full overflow-auto"
        @mousedown="onMousedown"
        @mouseup.self="onMouseup"
      >
        <div class="flex flex-col max-w-xl my-12 mx-auto p-4 bg-white dark:bg-gray-900 border dark:border-gray-700 rounded-lg space-y-2">
          <h1 :id="titleId" class="text-xl">
            {{ title }}
          </h1>
          <slot />
          <button type="button" class="mt-1 ml-auto px-2 border rounded-lg" @click="() => emit('confirm')">
            Confirm
          </button>
        </div>
      </div>
    </VueFinalModal>
  </template>
Copyright © 2026