Form Input Bindings and v-model Modifiers

Beginner
12 min

Form Input Bindings and v-model Modifiers

v-model creates a two-way binding between a form control and a piece of state, but each control type stores its value differently: a text input holds a string, a checkbox holds a boolean or an array, a <select multiple> holds an array. In this lesson you will bind every native control correctly, use the .lazy, .number and .trim modifiers, and customize the values a checkbox or radio writes into your state.

What v-model Does Under the Hood

For a text input, v-model="name" is equivalent to binding :value="name" and listening for @input="name = $event.target.value". Vue picks the right property and event for each element type automatically:

| Element | Property bound | Event listened to | | --- | --- | --- | | <input type="text">, <textarea> | value | input | | <input type="checkbox">, <input type="radio"> | checked | change | | <select> | value | change |

Because v-model sets the value from state, an initial value="" or selected attribute in the template is ignored. Initialize the state instead.

Text, Textarea, Checkbox, Radio and Select

vue
<script setup> import { ref } from 'vue' const message = ref('') const accepted = ref(false) const interests = ref([]) const plan = ref('free') const tags = ref([]) </script> <template> <textarea v-model="message"></textarea> <label><input type="checkbox" v-model="accepted" /> I accept</label> <label><input type="checkbox" value="vue" v-model="interests" /> Vue</label> <label><input type="checkbox" value="ts" v-model="interests" /> TypeScript</label> <label><input type="radio" value="free" v-model="plan" /> Free</label> <label><input type="radio" value="pro" v-model="plan" /> Pro</label> <select v-model="tags" multiple> <option value="a">Alpha</option> <option value="b">Beta</option> </select> </template>

Rules to remember:

  • A single checkbox bound to a boolean toggles true/false.
  • Several checkboxes bound to the same array add and remove their value from it: checking both gives ['vue', 'ts'].
  • Radio buttons sharing one ref store the value of the selected option.
  • <select multiple> needs an array; a single <select> stores one value. Provide a disabled placeholder option (<option disabled value="">Choose</option>) when the initial value does not match any option, otherwise iOS Safari cannot fire a change event for the first option.

Interpolation inside <textarea>{{ message }}</textarea> does not work; always use v-model.

Binding Non-String Values

By default a checkbox writes true/false and radios write the string in value. Use true-value/false-value on checkboxes and :value on radios and options to store other values, including objects:

vue
<template> <input type="checkbox" v-model="status" true-value="active" false-value="inactive" /> <input type="radio" v-model="selected" :value="{ id: 1, name: 'Basic' }" /> <select v-model="chosen"> <option v-for="opt in options" :key="opt.id" :value="opt">{{ opt.name }}</option> </select> </template>

true-value and false-value only affect the bound state; the checkbox still submits nothing when unchecked in a plain HTML form submission.

Modifiers: .lazy, .number and .trim

Modifiers are appended to v-model with a dot and change how the value is synced:

vue
<template> <!-- update after change instead of on every keystroke --> <input v-model.lazy="search" /> <!-- cast to a number with parseFloat; keeps the string if it cannot be parsed --> <input v-model.number="quantity" type="number" /> <!-- strip leading and trailing whitespace --> <input v-model.trim="username" /> <!-- modifiers can be combined --> <input v-model.trim.lazy="comment" /> </template>

.lazy is useful for expensive reactions such as filtering a large list. .number matters because the DOM always returns strings, even from type="number". Note that .number is applied automatically when the input has type="number", so you only need it explicitly on text inputs.

Common mistakes

  • Binding a checkbox group to a boolean instead of an array; only the last click registers.
  • Comparing a .number value with === before the user has typed anything: an empty input yields '', not 0.
  • Using @submit without .prevent, which reloads the page and loses the state.
  • Expecting v-model on a native element to emit a custom event; the component form of v-model (modelValue and update:modelValue) is a different mechanism covered in the component events lesson.
Quick Quiz
Question 1 of 2

Three checkboxes share `v-model="colors"`. What must `colors` be initialized as?

Key Takeaways

  • v-model binds the correct property and event for each control type; initialize state instead of using HTML value/selected attributes.
  • Single checkboxes map to booleans, checkbox groups and <select multiple> map to arrays, radios map to the selected value.
  • true-value, false-value and :value let controls store non-string values, including objects.
  • .lazy syncs on change, .number casts to a number, .trim strips whitespace; they can be combined.
  • Always use @submit.prevent on forms handled by Vue.

Next lesson: Computed Properties and Watchers — derive values from state and react to changes with side effects.

Form Input Bindings and v-model Modifiers - Vue.js | CodeYourCraft | CodeYourCraft