Skip to main content
The Field component provides a declarative way to render form fields in Vue applications.

Import

import { Field } from '@tanstack/vue-form'
// or access via form API
const form = useForm(...)
// <form.Field ... />

Props

form
FormApi<TParentData>
required
The parent form API instance.
name
TName
required
The field name as a path string (e.g., ‘user.firstName’ or ‘items[0].name’).
defaultValue
TData
The default value for the field.
validators
FieldValidators<TParentData, TName, TData>
Validation functions for the field.
validators.onChange
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs on every change.
validators.onChangeAsync
FieldAsyncValidateOrFn<TParentData, TName, TData>
Async validator that runs on change.
validators.onChangeAsyncDebounceMs
number
Debounce time in milliseconds for async validation.
validators.onBlur
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs when the field loses focus.
validators.onMount
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs when the field is mounted.
asyncDebounceMs
number
Debounce time in milliseconds for async validation. Default is 500ms.
asyncAlways
boolean
If true, async validation runs even if sync validation fails.

Slot Props

The default slot receives an object with the following properties:
field
FieldApi<TParentData, TName, TData>
The field API instance.
field.name
TName
The field name.
field.state
FieldState<TData>
The current field state.
field.handleChange
(value: TData) => void
Update the field value.
field.handleBlur
() => void
Mark the field as touched.
state
FieldState<TData>
The current field state (same as field.state).
state.value
TData
The current field value.
state.meta
FieldMeta
Field metadata including errors, touched status, etc.

Usage Example

Basic Field

<script setup lang="ts">
import { useForm } from '@tanstack/vue-form'

const form = useForm({
  defaultValues: {
    firstName: '',
  },
  onSubmit: async ({ value }) => {
    console.log(value)
  },
})
</script>

<template>
  <form.Field name="firstName">
    <template v-slot="{ field, state }">
      <label :htmlFor="field.name">First Name:</label>
      <input
        :id="field.name"
        :name="field.name"
        :value="field.state.value"
        @input="(e) => field.handleChange((e.target as HTMLInputElement).value)"
        @blur="field.handleBlur"
      />
    </template>
  </form.Field>
</template>

Field with Validation

<template>
  <form.Field
    name="email"
    :validators="{
      onChange: ({ value }) => {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
        return !emailRegex.test(value) ? 'Invalid email address' : undefined
      },
      onChangeAsync: async ({ value }) => {
        await new Promise((resolve) => setTimeout(resolve, 1000))
        return value.includes('test') ? 'Test emails not allowed' : undefined
      },
      onChangeAsyncDebounceMs: 500,
    }"
  >
    <template v-slot="{ field, state }">
      <label :htmlFor="field.name">Email:</label>
      <input
        :id="field.name"
        :value="field.state.value"
        @input="(e) => field.handleChange((e.target as HTMLInputElement).value)"
        @blur="field.handleBlur"
      />
      <div v-if="state.meta.isTouched && state.meta.errors.length">
        <span style="color: red">{{ state.meta.errors[0] }}</span>
      </div>
      <div v-if="state.meta.isValidating">
        <span>Validating...</span>
      </div>
    </template>
  </form.Field>
</template>

Array Field

<template>
  <form.Field name="items" mode="array">
    <template v-slot="{ field }">
      <div v-for="(item, index) in field.state.value" :key="index">
        <form.Field :name="`items[${index}].name`">
          <template v-slot="{ field: subField }">
            <input
              :value="subField.state.value"
              @input="(e) => subField.handleChange((e.target as HTMLInputElement).value)"
            />
          </template>
        </form.Field>
        <button @click="() => field.removeValue(index)" type="button">
          Remove
        </button>
      </div>
      <button @click="() => field.pushValue({ name: '' })" type="button">
        Add Item
      </button>
    </template>
  </form.Field>
</template>

See Also

  • useForm - Create and manage forms
  • useField - Create and manage individual fields
  • FieldApi - Core field API documentation

Build docs developers (and LLMs) love