v-model
The v-model directive lets you create a two-way binding on a form input element or a component. You can use it with:
<input><textarea><select>- Components
<input v-model="text" />v-model automatically expands to different DOM property and event pairs based on the element it is used on:
<input>with text types and<textarea>elements usevalueproperty andinputevent.<input>with checkbox and radio types usecheckedproperty andchangeevent.<select>elements usevalueproperty andchangeevent.
This abstraction allows you to write consistent, declarative code without worrying about the specific event handling logic for each input type.
WARNING
v-model will ignore the initial value, checked or selected attributes found on any form elements. It will always treat the current bound JavaScript state as the source of truth.
Basic Usage
Text
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />Multiline Text
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>Checkbox
Single checkbox (boolean value):
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>You can bind multiple checkboxes to the same array (or a Set in Vue 3.3+):
<script setup>
import { ref } from 'vue'
const checkedNames = ref([])
</script>
<template>
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
</template>In this example, the checkedNames array always reflects the values of the currently checked boxes.
Radio
<div>Picked: {{ picked }}</div>
<input type="radio" name="picked" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" name="picked" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>Radio buttons sharing the same v-model will update the bound value based on the selected option.
Select
Single Select:
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>Multiple Select (bound to array):
<div>Selected: {{ selected }}</div>
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>Select options can be dynamically rendered with v-for:
<script setup>
import { ref } from 'vue'
const selected = ref('A')
const options = ref([
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
])
</script>
<template>
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option v-for="option in options" :value="option.value" :key="option.value">
{{ option.text }}
</option>
</select>
</template>Value Bindings
For radio, checkbox and select options, the v-model binding values are usually static strings (or booleans for checkboxes):
<!-- `picked` is "a" when checked -->
<input type="radio" v-model="picked" value="a" />
<!-- `toggle` is true or false -->
<input type="checkbox" v-model="toggle" />
<!-- `selected` is "abc" when the first option is selected -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>But sometimes we may want to bind dynamic or non-string values. v-bind can be used to achieve that.
Checkbox
Vue provides true-value and false-value attributes for checkboxes:
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no" />When checked, toggle becomes "yes"; when unchecked, it becomes "no".
You can also bind these values dynamically using v-bind:
<input
type="checkbox"
v-model="toggle"
:true-value="dynamicTrueValue"
:false-value="dynamicFalseValue" />Radio
<input type="radio" name="picked" v-model="pick" :value="first" />
<input type="radio" name="picked" v-model="pick" :value="second" />pick will be set to the value of first when the first radio input is checked, and set to the value of second when the second one is checked.
Select
<select v-model="selected">
<!-- inline object literal -->
<option :value="{ number: 123 }">123</option>
</select>v-model supports binding non-string values. In the above example, when the option is selected, selected will be set to the object { number: 123 }.
Modifiers
.lazy
By default, v-model syncs the input with the data on every input event (on every keystroke). Use .lazy modifier to sync only after the change event (when the input loses focus):
<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" />.number
The number modifier can be used to automatically typecast user input as a number:
<input v-model.number="age" />If the value cannot be parsed with parseFloat(), then the original (string) value is used instead. In particular, if the input is empty (for instance after the user clearing the input field), an empty string is returned.
NOTE
The number modifier is applied automatically if the input has type="number".
.trim
The trim modifier can be used to automatically trim whitespace from user input:
<input v-model.trim="msg" />With Components
HTML's built-in input types won't always meet your needs. Fortunately, Vue Components let you build reusable, fully customized input controls and they integrate seamlessly with v-model.
By implementing the appropriate props and events, a component can behave just like a native form element while encapsulating its own logic and presentation.
Component usage with v-model is covered in detail in a later guide.
