Forms & v-model

Binding an input's value and listening for it to change is common enough that Vue gives it a single directive, v-model, which keeps a form field and a piece of data in sync in both directions.

v-model on a text input

Without v-model, you'd bind the input's value with :value and separately listen for an @input event to write it back — v-model does both at once:

HTML template
<input v-model="username">
<p>You typed: {{ username }}</p>
Behavior, as the user types "sam"
You typed: s
You typed: sa
You typed: sam

Typing in the input updates this.username immediately, and because it's reactive, anything else in the template that reads username (like the paragraph above) updates right along with it.

v-model on other input types

v-model adapts to whatever kind of form element it's on — a checkbox binds to a boolean, a group of radios binds to whichever value is selected, and a select binds to the chosen option's value:

HTML template
<input type="checkbox" v-model="subscribed"> Subscribe to updates

<select v-model="country">
  <option value="in">India</option>
  <option value="us">United States</option>
</select>

Here subscribed holds true/false, and country holds whichever option's value attribute is currently selected — "in" or "us", not the visible text.

Submitting a form

Combine v-model on each field with the @submit.prevent modifier from the event-handling lesson to build a complete, controlled form:

HTML template
<form @submit.prevent="handleSubmit">
  <input v-model="email" type="email" placeholder="Email">
  <button type="submit">Sign up</button>
</form>
JS methods
methods: {
  handleSubmit() {
    console.log('Submitting:', this.email)
  }
}
Note: under the hood, v-model is really just shorthand — on a text input it expands to :value="username" plus @input="username = $event.target.value". Knowing that expansion is useful once you write a custom component that wants to support v-model itself: it just needs to accept a modelValue prop and emit an update:modelValue event.