Vue Final Modal
Use Cases

Modal Login Form

Create a <ModalLoginForm> component with <VueFinalModal> and TailwindCSS.

A login form inside a modal. The form is validated with Vorms, and the modal closes once the form is submitted.

Preview

Submitted: nothing yet
Preview.vue
  <script setup lang="ts">
  import type { FormData } from './LoginFormVorms.vue'
  import { useModal } from 'vue-final-modal'
  import ModalLoginForm from './ModalLoginForm.vue'

  const submitted = ref('nothing yet')

  const { open, close } = useModal({
    component: ModalLoginForm,
    attrs: {
      onSubmit(formData: FormData) {
        submitted.value = JSON.stringify(formData)
        close()
      },
    },
  })
  </script>

  <template>
    <div class="flex flex-wrap items-center justify-center gap-3">
      <VButton @click="() => open()">
        Open Modal
      </VButton>
      <output class="inline-flex items-center gap-1.5 rounded-md border border-default px-2 py-1 text-sm text-muted">
        Submitted: <span class="font-medium text-highlighted">{{ submitted }}</span>
      </output>
    </div>
  </template>

<ModalLoginForm> component

  <script setup lang="ts">
  import type { FormData } from './LoginFormVorms.vue'
  import { useId } from 'vue'
  import { VueFinalModal } from 'vue-final-modal'
  import LoginFormVorms from './LoginFormVorms.vue'

  const emit = defineEmits<{
    (e: 'submit', formData: FormData): void
  }>()

  const titleId = useId()
  </script>

  <template>
    <VueFinalModal
      class="flex justify-center items-center"
      content-class="relative w-full max-w-xs mx-4 p-4 rounded-lg bg-white dark:bg-gray-900 border dark:border-gray-700"
      content-transition="vfm-fade"
      overlay-transition="vfm-fade"
      :aria-labelledby="titleId"
    >
      <h1 :id="titleId" class="text-2xl mb-4">
        Login
      </h1>
      <LoginFormVorms @submit="formData => emit('submit', formData)" />
    </VueFinalModal>
  </template>

<LoginFormVorms> component

  <script setup lang="ts">
  import { useForm } from '@vorms/core'

  export type FormData = {
    account: string
    password: string
    remember: boolean
  }

  const emit = defineEmits<{
    (e: 'submit', formData: FormData): void
  }>()

  const { register, errors, handleSubmit } = useForm({
    initialValues: {
      account: '',
      password: '',
      remember: false,
    },
    onSubmit(values) {
      emit('submit', values)
    },
  })

  const { value: account, attrs: accountAttrs } = register('account', {
    validate(value) {
      if (!value)
        return 'account is required!'
    },
  })
  const { value: password, attrs: passwordAttrs } = register('password')
  const { value: remember, attrs: rememberAttrs } = register('remember')
  </script>

  <template>
    <form class="flex flex-col gap-4" @submit="handleSubmit">
      <div>
        <input
          v-model="account"
          class="w-full px-2 py-1 border dark:border-gray-700 rounded-lg bg-transparent"
          type="text"
          placeholder="Account"
          v-bind="accountAttrs"
        >
        <div v-show="'account' in errors" class="mt-0.5 text-red-500">
          {{ errors.account }}
        </div>
      </div>
      <input
        v-model="password"
        class="w-full px-2 py-1 border dark:border-gray-700 rounded-lg bg-transparent"
        type="password"
        placeholder="Password"
        v-bind="passwordAttrs"
      >
      <div class="flex items-center gap-1">
        <input
          id="remember"
          v-model="remember"
          class="accent-[#41b883]"
          type="checkbox"
          v-bind="rememberAttrs"
        >
        <label for="remember">remember</label>
      </div>
      <input type="submit" value="Submit" class="w-full px-2 py-1 rounded-lg border border-[#41b883] bg-[#41b883] text-white cursor-pointer">
    </form>
  </template>
Copyright © 2026