---
description: Collect and validate form data.
links:
  - label: GitHub
    icon: i-simple-icons-github
    to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Form.vue
---

## Usage

Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://valibot.dev/) or your own validation logic. It works seamlessly with the FormGroup component to automatically display error messages around form elements.

The Form component requires the `validate` and `state` props for form validation.

- `state` - a reactive object that holds the current state of the form.
- `validate` - a function that takes the form's state as input and returns an array of `FormError` objects with the following fields:
  - `message` - the error message to display.
  - `path` - the path to the form element matching the `name`.

::component-example
#default
:form-example-basic{class="space-y-4 w-60"}

#code
```vue
<script setup lang="ts">
import { ref } from 'vue'
import type { FormError, FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'

const state = ref({
  email: undefined,
  password: undefined
})

const validate = (state: any): FormError[] => {
  const errors = []
  if (!state.email) errors.push({ path: 'email', message: 'Required' })
  if (!state.password) errors.push({ path: 'password', message: 'Required' })
  return errors
}

async function submit (event: FormSubmitEvent<any>) {
  // Do something with data
  console.log(event.data)
}
</script>

<template>
  <UForm
    :validate="validate"
    :state="state"
    @submit="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>
```
::

## Schema

You can provide a schema from [Yup](#yup), [Zod](#zod) or [Joi](#joi), [Valibot](#valibot) through the `schema` prop to validate the state. It's important to note that **none of these libraries are included** by default, so make sure to **install the one you need**.

### Yup

::component-example
#default
:form-example-yup{class="space-y-4 w-60"}

#code
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { object, string, InferType } from 'yup'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'

const schema = object({
  email: string().email('Invalid email').required('Required'),
  password: string()
    .min(8, 'Must be at least 8 characters')
    .required('Required')
})

type Schema = InferType<typeof schema>

const state = ref({
  email: undefined,
  password: undefined
})

async function submit (event: FormSubmitEvent<Schema>) {
  // Do something with event.data
  console.log(event.data)
}
</script>

<template>
  <UForm
    :schema="schema"
    :state="state"
    @submit="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>
```
::

### Zod

::component-example
#default
:form-example-zod{class="space-y-4 w-60"}

#code
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { z } from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'

const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Must be at least 8 characters')
})

type Schema = z.output<typeof schema>

const state = ref({
  email: undefined,
  password: undefined
})

async function submit (event: FormSubmitEvent<Schema>) {
  // Do something with data
  console.log(event.data)
}
</script>

<template>
  <UForm
    :schema="schema"
    :state="state"
    @submit="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>
```
::

### Joi

::component-example
#default
:form-example-joi{class="space-y-4 w-60"}

#code
```vue
<script setup lang="ts">
import { ref } from 'vue'
import Joi from 'joi'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'

const schema = Joi.object({
  email: Joi.string().required(),
  password: Joi.string()
    .min(8)
    .required()
})

const state = ref({
  email: undefined,
  password: undefined
})

async function submit (event: FormSubmitEvent<any>) {
  // Do something with event.data
  console.log(event.data)
}
</script>

<template>
  <UForm
    :schema="schema"
    :state="state"
    @submit="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>
```
::

### Valibot

::component-example
#default
:form-example-valibot{class="space-y-4 w-60"}

#code
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { string, object, email, minLength, Input } from 'valibot'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'

const schema = object({
  email: string([email('Invalid email')]),
  password: string([minLength(8, 'Must be at least 8 characters')])
})

type Schema = Input<typeof schema>

const state = ref({
  email: undefined,
  password: undefined
})

async function submit (event: FormSubmitEvent<Schema>) {
  // Do something with event.data
  console.log(event.data)
}
</script>

<template>
  <UForm
    :schema="schema"
    :state="state"
    @submit="submit"
  >
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>
```
::

## Other libraries

For other validation libraries, you can define your own component with custom validation logic.

Here is an example with [Vuelidate](https://github.com/vuelidate/vuelidate):

```vue
<script setup lang="ts">
import useVuelidate from '@vuelidate/core'

const props = defineProps({
  rules: { type: Object, required: true },
  model: { type: Object, required: true }
})

const form = ref();
const v = useVuelidate(props.rules, props.model)

async function validateWithVuelidate() {
  v.value.$touch()
  await v.value.$validate()
  return v.value.$errors.map((error) => ({
    message: error.$message,
    path: error.$propertyPath,
  }))
}

defineExpose({
  validate: async () => {
    await form.value.validate()
  }
})
</script>

<template>
  <UForm ref="form" :model="model" :validate="validateWithVuelidate">
    <slot />
  </UForm>
</template>
```

## Backend validation

You can manually set errors after form submission if required. To do this, simply use the `form.setErrors` function to set the errors as needed.

```vue
<script setup lang="ts">
import type { FormError, FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'

const state = ref({
  email: undefined,
  password: undefined
})

const form = ref()

async function submit (event: FormSubmitEvent<any>) {
  form.value.clear()
  const response = await fetch('...')

  if (!response.status === 422) {
    const errors = await response.json()
    form.value.setErrors(errors.map((err) => {
      // Map validation errors to { path: string, message: string }
    }))
  } else {
    // ...
  }
}
</script>

<template>
  <UForm ref="form" :state="state" @submit="submit">
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>
```

## Input events

The Form component automatically triggers validation upon `submit`, `input`, `blur` or `change` events. This ensures that any errors are displayed as soon as the user interacts with the form elements. You can control when validation happens this using the `validate-on` prop.

::component-example
#default
:form-example-elements{class="space-y-4 w-60"}
::

## Props

:component-props

## API

::field-group
  ::field{name="validate (path?: string, opts: { silent?: boolean })" type="Promise<T>"}
    Triggers form validation. Will raise any errors unless `opts.silent` is set to true.
  ::
  ::field{name="clear (path?: string)" type="void"}
    Clears form errors associated with a specific path. If no path is provided, clears all form errors.
  ::
  ::field{name="getErrors (path?: string)" type="FormError[]"}
    Retrieves form errors associated with a specific path. If no path is provided, returns all form errors.
  ::
  ::field{name="setErrors (errors: FormError[], path?: string)" type="void"}
    Sets form errors for a given path. If no path is provided, overrides all errors.
  ::
  ::field{name="errors" type="Ref<FormError[]>"}
    A reference to the array containing validation errors. Use this to access or manipulate the error information.
  ::
::
