Controlled fields

By default, all form fields are uncontrolled because that's the default behavior of the browser. For a simple login or contact form this is quite sufficient.

Why controlled?

As soon as your forms become more complex, for example you set initial values or change the values of a form field via setInput, it becomes necessary that you control your fields yourself. For example, depending on which HTML form field you use, you may need to set the value, checked or selected attributes.

Simple text inputs

For most input types including text, email, number, and date, you simply add the v-model directive. Vue automatically handles type conversions for you:

<template>
  <Field :of="loginForm" :path="['firstName']" v-slot="field">
    <input v-model="field.input" v-bind="field.props" type="text" />
  </Field>
</template>

Vue's automatic type handling

Vue's v-model automatically handles different input types without special code:

Number inputs - Vue converts between strings and numbers automatically:

<template>
  <Field :of="form" :path="['age']" v-slot="field">
    <input v-model="field.input" v-bind="field.props" type="number" />
  </Field>
</template>

Date inputs - Vue handles the date string format automatically:

<template>
  <Field :of="form" :path="['birthday']" v-slot="field">
    <input v-model="field.input" v-bind="field.props" type="date" />
  </Field>
</template>

This works seamlessly whether your schema expects strings, numbers, or dates - Vue will handle the conversion.

Checkboxes

For checkboxes, you need to bind to the checked attribute and handle both boolean and array values:

Single checkbox (boolean):

<template>
  <Field :of="form" :path="['acceptTerms']" v-slot="field">
    <input v-bind="field.props" type="checkbox" :checked="field.input" />
  </Field>
</template>

Multiple checkboxes (array of strings):

<template>
  <Field :of="form" :path="['interests']" v-slot="field">
    <label v-for="option in options" :key="option.value">
      <input
        v-bind="field.props"
        type="checkbox"
        :value="option.value"
        :checked="field.input?.includes(option.value)"
      />
      {{ option.label }}
    </label>
  </Field>
</template>

Select elements

For select elements, you need to bind to the selected attribute:

Single select:

<template>
  <Field :of="form" :path="['country']" v-slot="field">
    <select v-bind="field.props">
      <option
        v-for="option in options"
        :key="option.value"
        :value="option.value"
        :selected="field.input === option.value"
      >
        {{ option.label }}
      </option>
    </select>
  </Field>
</template>

Multiple select:

<template>
  <Field :of="form" :path="['languages']" v-slot="field">
    <select v-bind="field.props" multiple>
      <option
        v-for="option in options"
        :key="option.value"
        :value="option.value"
        :selected="field.input?.includes(option.value)"
      >
        {{ option.label }}
      </option>
    </select>
  </Field>
</template>

File inputs

The HTML <input type="file" /> element is an exception because it cannot be controlled in the traditional way. However, you can control the UI around it. For inspiration, check out our FileInput component from the playground.

Next steps

Now that you understand controlled fields, you can explore more advanced topics like nested fields and field arrays to handle complex form structures.

Contributors

Thanks to all the contributors who helped make this page better!

  • GitHub profile picture of @fabian-hiller

Partners

Thanks to our partners who support the project ideally and financially.

Sponsors

Thanks to our GitHub sponsors who support the project financially.

  • GitHub profile picture of @vasilii-kovalev
  • GitHub profile picture of @saturnonearth
  • GitHub profile picture of @ruiaraujo012
  • GitHub profile picture of @hyunbinseo
  • GitHub profile picture of @nickytonline
  • GitHub profile picture of @KubaJastrz
  • GitHub profile picture of @andrewmd5
  • GitHub profile picture of @Thanaen
  • GitHub profile picture of @caegdeveloper
  • GitHub profile picture of @bmoyroud
  • GitHub profile picture of @dslatkin