# Form Input Bindings

Learn how to work with form inputs with a free Vue School lesson

# Basic Usage

You can use the v-model directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, v-model is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.

Note

v-model will ignore the initial value, checked or selected attributes found on any form elements. It will always treat the current active instance data as the source of truth. You should declare the initial value on the JavaScript side, inside the data option of your component.

v-model internally uses different properties and emits different events for different input elements:

  • text and textarea elements use value property and input event;
  • checkboxes and radiobuttons use checked property and change event;
  • select fields use value as a prop and change as an event.

Note

For languages that require an IME (opens new window) (Chinese, Japanese, Korean etc.), you'll notice that v-model doesn't get updated during IME composition. If you want to respond to these updates as well, use an input event listener and value binding instead of using v-model.

# Text

<input v-model="message" placeholder="edit me" />
<p>Message is: {{ message }}</p>
1
2

See the Pen Handling forms: basic v-model by Vue (@Vue) on CodePen.

# Multiline text

<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br />
<textarea v-model="message" placeholder="add multiple lines"></textarea>
1
2
3
4

See the Pen Handling forms: textarea by Vue (@Vue) on CodePen.

Interpolation on textareas won't work. Use v-model instead.

<!-- bad -->
<textarea>{{ text }}</textarea>

<!-- good -->
<textarea v-model="text"></textarea>
1
2
3
4
5

# Checkbox

Single checkbox, boolean value:

<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
1
2

See the Pen Handling forms: checkbox by Vue (@Vue) on CodePen.

Multiple checkboxes, bound to the same array:

<div id="v-model-multiple-checkboxes">
  <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>
  <br />
  <span>Checked names: {{ checkedNames }}</span>
</div>
1
2
3
4
5
6
7
8
9
10
Vue.createApp({
  data() {
    return {
      checkedNames: []
    }
  }
}).mount('#v-model-multiple-checkboxes')
1
2
3
4
5
6
7

See the Pen Handling forms: multiple checkboxes by Vue (@Vue) on CodePen.

# Radio

<div id="v-model-radiobutton">
  <input type="radio" id="one" value="One" v-model="picked" />
  <label for="one">One</label>
  <br />
  <input type="radio" id="two" value="Two" v-model="picked" />
  <label for="two">Two</label>
  <br />
  <span>Picked: {{ picked }}</span>
</div>
1
2
3
4
5
6
7
8
9
Vue.createApp({
  data() {
    return {
      picked: ''
    }
  }
}).mount('#v-model-radiobutton')
1
2
3
4
5
6
7

See the Pen Handling forms: radiobutton by Vue (@Vue) on CodePen.

# Select

Single select:

<div id="v-model-select" class="demo">
  <select v-model="selected">
    <option disabled value="">Please select one</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>
1
2
3
4
5
6
7
8
9
Vue.createApp({
  data() {
    return {
      selected: ''
    }
  }
}).mount('#v-model-select')
1
2
3
4
5
6
7

See the Pen Handling forms: select by Vue (@Vue) on CodePen.

Note

If the initial value of your v-model expression does not match any of the options, the <select> element will render in an "unselected" state. On iOS this will cause the user not being able to select the first item because iOS does not fire a change event in this case. It is therefore recommended to provide a disabled option with an empty value, as demonstrated in the example above.

Multiple select (bound to array):

<select v-model="selected" multiple>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>
<br />
<span>Selected: {{ selected }}</span>
1
2
3
4
5
6
7

See the Pen Handling forms: select bound to array by Vue (@Vue) on CodePen.

Dynamic options rendered with v-for:

<div id="v-model-select-dynamic" class="demo">
  <select v-model="selected">
    <option v-for="option in options" :value="option.value">
      {{ option.text }}
    </option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>
1
2
3
4
5
6
7
8
Vue.createApp({
  data() {
    return {
      selected: 'A',
      options: [
        { text: 'One', value: 'A' },
        { text: 'Two', value: 'B' },
        { text: 'Three', value: 'C' }
      ]
    }
  }
}).mount('#v-model-select-dynamic')
1
2
3
4
5
6
7
8
9
10
11
12

See the Pen Handling forms: select with dynamic options by Vue (@Vue) on CodePen.

# Value Bindings

For radio, checkbox and select options, the v-model binding values are usually static strings (or booleans for checkbox):

<!-- `picked` is a string "a" when checked -->
<input type="radio" v-model="picked" value="a" />

<!-- `toggle` is either true or false -->
<input type="checkbox" v-model="toggle" />

<!-- `selected` is a string "abc" when the first option is selected -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>
1
2
3
4
5
6
7
8
9
10

But sometimes we may want to bind the value to a dynamic property on the current active instance. We can use v-bind to achieve that. In addition, using v-bind allows us to bind the input value to non-string values.

# Checkbox

<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
1
// when checked:
vm.toggle === 'yes'
// when unchecked:
vm.toggle === 'no'
1
2
3
4

Tip

The true-value and false-value attributes don't affect the input's value attribute, because browsers don't include unchecked boxes in form submissions. To guarantee that one of two values is submitted in a form (e.g. "yes" or "no"), use radio inputs instead.

# Radio

<input type="radio" v-model="pick" v-bind:value="a" />
1
// when checked:
vm.pick === vm.a
1
2

# Select Options

<select v-model="selected">
  <!-- inline object literal -->
  <option :value="{ number: 123 }">123</option>
</select>
1
2
3
4
// when selected:
typeof vm.selected // => 'object'
vm.selected.number // => 123
1
2
3

# Modifiers

# .lazy

By default, v-model syncs the input with the data after each input event (with the exception of IME composition as stated above). You can add the lazy modifier to instead sync after change events:

<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" />
1
2

# .number

If you want user input to be automatically typecast as a number, you can add the number modifier to your v-model managed inputs:

<input v-model.number="age" type="text" />
1

This is often useful when the input type is text. If the input type is number, Vue can automatically convert the raw string value to a number, and you don't need to add the .number modifier to v-model. If the value cannot be parsed with parseFloat(), then the original value is returned.

# .trim

If you want whitespace from user input to be trimmed automatically, you can add the trim modifier to your v-model-managed inputs:

<input v-model.trim="msg" />
1

# v-model with Components

If you're not yet familiar with Vue's components, you can skip this for now.

HTML's built-in input types won't always meet your needs. Fortunately, Vue components allow you to build reusable inputs with completely customized behavior. These inputs even work with v-model! To learn more, read about custom inputs in the Components guide.