Post cover

How to Use v-model to Access Input Values in Vue

Updated May 22, 2023

v-model is handy to access and modify input fields values.

<input v-model="myRef" /> enables the two-way binding. When user types into the input then the ref changes, and vice-versa when the ref is changed programmatically then the input value changes.

Let's see how to use v-model to bind form inputs in Vue 3.

Before I go on, let me recommend something to you.

Using Vue composition API is fun... but sometimes challenging. Take "Vue 3 Composition API" course by Vueschool to become proficient in Vue composition and reactivity in just a few weekends!

1. Binding a form input

Let's implement a component that renders an input field with the initial value 'Unknown'. The value that user introduces into the input field renders on the screen.

v-model fits nicely to implement such a component. Connecting v-model with the input field requires 2 simple steps:

  1. const text = ref(): reactive value to serve as a data bus for v-model
  2. v-model="text": add v-model to the input field tag assigned with text.

<script setup>
import { ref } from 'vue'
const text = ref('Unknown') // Step 1: create data bus
</script>
<template>
<!-- Step 2: assign data bus to v-model -->
<input v-model="text" type="input" />
<div>{{ text }}</div>
</template>

Open the demo.

Open the demo. The input field contains initially 'Unknown'. Type something into the input field: and both the input value and the text on the screen update.

v-model="text" is two-way data binding in Vue.

Vue two way binding

The first direction of flow happens during the initialization. The input value is initialized with 'Unknown'text's initial value.

The second direction of flow occurs when you type into the input field. v-model takes the value of the input and updates text ref with it.

2. v-model vs v-bind

v-bind is another data binding mechanism in Vue:


<input v-bind:value="text" type="text" />

which can be shortened to:


<input :value="text" type="text" />

What is the difference between v-model and :value? <input :value="value" /> is a one-way data flow mechanism.

To understand the difference let's change the previous example's input tag from v-model="text" to :value="text":


<script setup>
import { ref } from 'vue'
const text = ref('Unknown')
</script>
<template>
<input :value="text" type="input" />
<div>{{ text }}</div>
</template>

Open the demo.

Open the demo. The input field is initialized with 'Unknown'.

Type some characters into the input field... and the text rendered on the screen always stays the same — 'Unknown'. text ref is not updated when the input field changes.

:value="text" makes data flow in one direction only: from the text ref to the input field. Changing the input field value, however, doesn't change text ref.

Vue one-way binding

In conclusion, v-model enables a two-way data flow, while :value enables a one-way data flow.

2.1 Emulating v-model

Despite the difference, v-model can be emulated using :value and @input:


<input v-model="text" type="text" />

can be expressed as:


<input :value="text" @input="text = $event.target.value" type="text" />

The following code doesn't use v-model, but still has two-way data flow working:


<script setup>
import { ref } from 'vue'
const text = ref('Unknown')
</script>
<template>
<input
:value="text"
@input="text = $event.target.value"
type="input"
/>
<div>{{ text }}</div>
</template>

Open the demo.

Open the demo and type some characters into the input. The two-way binding is working correctly.

The regular binding :value="text" enables the first flow.

@input="text = $event.target.value" updates text when user types into the input field gets triggered. That's the second flow.

3. Binding using reactive()

reactive() is a Vue reactivity API that makes an object reactive.

The main difference between ref() and reactive() is that refs can store primitives and objects, while reactive() accepts only objects. And reactive() object can be access directly (without .value property as in the case of refs).

Binding a reactive object to form inputs is handy when you have a lot of inputs. You can bind each input field with a specific property of the reactive object.

Let's implement a form having first and last name inputs, and bound these to a reactive object's properties:


<script setup>
import { reactive } from 'vue'
const person = reactive({ firstName: 'John', lastName: 'Smith' })
</script>
<template>
<input v-model="person.firstName" type="input" />
<input v-model="person.lastName" type="input" />
<div>Full name is {{ person.firstName }} {{ person.lastName }}</div>
</template>

Open the demo.

const person = reactive({ firstName: '', lastName: '' }) creates a reactive object.

v-model="person.firstName" binds with the first name property, as well as v-model="person.lastName" binds to the last name property.

Open the demo. Type into the first or last name inputs, and you'll see that the rendered text changes according to the name you type.

Vue two-way binding reactive

Pretty nice! Properties of a reactive object can serve as data buses for v-model. Use this approach to bind many input fields.

4. Binding different input types

Many other input types like select, textarea, checkboxes, radio buttons can bind using v-model. Let's explore them.

4.1 Textareas

Binding a textarea to a ref is straightforward. Just use v-model on the textarea tag <textarea v-model="longText" />:


<script setup>
import { ref } from 'vue'
const longText = ref("Well... here's my story. One morning...")
</script>
<template>
<textarea v-model="longText" />
<div>{{ longText }}</div>
</template>

Open the demo.

4.2 Select fields

The select (aka dropdown) field offers user a pre-defined set of options to choose from.

Binding a select is simple: <select v-model="selected" />:


<script setup>
import { ref } from 'vue'
const employeeId = ref('2')
</script>
<template>
<select v-model="employeeId">
<option value="1">Jane Doe</option>
<option value="2">John Doe</option>
<option value="3">John Smith</option>
</select>
<div>Selected id: {{ employeeId }}</div>
</template>

Open the demo.

employeeId is the ref bound to the select and will get the value of the option being selected.

Because employeeId ref is initialized with '2', John Doe option is initially selected.

When you select another option, you can see that employeeId updates with the newly selected option value.

If the select options do not have value attributes, then the binding works with the text of the options:


<script setup>
import { ref } from 'vue'
const employee = ref('Jane Doe')
</script>
<template>
<select v-model="employee">
<option>Jane Doe</option>
<option>John Doe</option>
<option>John Smith</option>
</select>
<div>Selected: {{ employee }}</div>
</template>

Open the demo.

Now the binding works directly with the textual value of the options. If you'd select the second option, then employee ref is assigned with 'John Doe'.

4.3 Checkboxes

Thanks to v-model binding checkboxes is easy:


<input ref="checked" type="checkbox" />

checked ref is assigned with a boolean value indicating whether the checkbox is checked or not.

Let's create a checkbox that bounds to a checked ref:


<script setup>
import { ref } from 'vue'
const checked = ref(true)
</script>
<template>
<label><input v-model="checked" type="checkbox" />Want a pizza?</label>
<div>{{ checked }}</div>
</template>

Open the demo.

checked ref is initialized with true and thus initially the checkbox is checked. Checking or unchecking the checkbox updates checked ref correspondingly with true or false. No rocket science here.

To customize the checked/unchecked binding to other values that a boolean, then Vue offers 2 Vue-specific attributes on the checkbox:


<input
v-model="checked"
true-value="Yes!"
false-value="No"
/>

Now checked is assigned to 'Yes!' (as true value) or 'No' (as false value) string depending on the checkbox status.

Let's modify the previous example to use the custom values 'Yes!' and 'No':


<script setup>
import { ref } from 'vue'
const answer = ref('Yes!')
</script>
<template>
<label>
<input v-model="answer" type="checkbox" true-value="Yes!" false-value="No" />
Want a pizza?
</label>
<div>{{ answer }}</div>
</template>

Open the demo.

answer now can have either 'Yes!' or 'No' depending on the checkbox's check status.

4.4 Radio buttons

To bind a group of radio buttons apply to the group the same the same bus binding v-model="option":


<input type="radio" v-model="option" value="a" />
<input type="radio" v-model="option" value="b" />
<input type="radio" v-model="option" value="c" />

For example, let's implement a radio buttons group to select the color of a T-shirt:


<script setup>
import { ref } from "vue"
const color = ref("white")
</script>
<template>
<label><input type="radio" v-model="color" value="white" />White</label>
<label><input type="radio" v-model="color" value="red" />Red</label>
<label><input type="radio" v-model="color" value="blue" />Blue</label>
<div>T-shirt color: {{ color }}</div>
</template>

Open the demo.

Initially, the White radio is selected because the color ref is initialized with 'white'.

Click on any other T-shirt color, and the color ref changes according to the selected color.

value attribute of the radio is bindable: you can use :value. That is helpful when the list of options comes from an array, for example:


<script setup>
import { ref } from "vue"
const color = ref("white")
const COLORS = [
{ option: "white", label: "White" },
{ option: "black", label: "Black" },
{ option: "blue", label: "Blue" },
]
</script>
<template>
<label v-for="{ option, label } in COLORS" :key="option">
<input type="radio" v-model="color" :value="option" /> {{ label }}
</label>
<div>T-shirt color: {{ color }}</div>
</template>

Open the demo.

5. v-model modifiers

On top of doing a wonderful job with binding form inputs, v-model has an additional feature called modifier.

A modifier is a piece of logic applied to v-model to customize its behavior. A modifier is applied to v-model by using a dot syntax v-model.<modifier>, for example v-mode.trim.

By default Vue offers 3 modifies: trim, number, and lazy.

5.1 trim

Trimming a string means removing whitespaces from the beginning and the end of the string. For example, trim applied to ' Wow! ' results in 'Wow!'.

Vue v-model.trim

v-model.trim modifier trims the input field value before assinging the value to the bound ref.


<script setup>
import { ref } from 'vue'
const text = ref('')
</script>
<template>
<input v-model.trim="text" type="text" />
<pre>"{{ text }}"</pre>
</template>

Open the demo.

Open the demo. Type a value that starts or ends with spaces, e.g. ' Hi! '. You'll see that the rendered text 'Hi!' doesn't have spaces on both ends.

5.2 number

v-model.number modifier applies a number parser on the input field value.

v-model.number="number" assigns to number a real number if the user introduced a value that can be parsed to a number. In other cases, if the introduced value is not numeric, number ref is just assigned to the original string.

Vue v-model.number

<script setup>
import { ref } from "vue";
const number = ref("");
</script>
<template>
<input v-model.number="number" type="text" />
<div>{{ typeof number }}</div>
</template>

Open the demo.

When you introduce '345' into the input, then number ref becomes 345 (a number). Value parsing happens automatically.

But if you introduce a non-numeric value into the input, like 'abc', then number ref is assigned with the same value 'abc'.

5.3 lazy

By default, v-model uses "input" event to determine when to update the bound ref. But using the modifier v-model.lazy you can alter the event to be "change" event.

What's the main difference between input and change events?

input is triggered every time you keypress in the input field.

change, however, is triggered only when the input field looses focus. Typing into the input field does not trigger change.

Vue v-model.lazy

The following example uses lazy binding:


<script setup>
import { ref } from 'vue'
const text = ref('Unknown')
</script>
<template>
<input v-model.lazy="text" type="input" />
<div>{{ text }}</div>
</template>

Open the demo.

Open the demo and type a few characters into the input. The rendered text stays the same: Unknown.

Now click somewhere outside of the input field to make it lose focus. The rendered text updates to the value that you introduced earlier.

If you have a form with many input fields and heavy state, you can apply the lazy modifier to disable realtime reactivity as the user types. That could prevent page freezes during typing.

6. Conclusion

From all the details you've read, remember just one.

v-model binds form inputs to refs or reactive objects.

Binding is performed in 2 easy steps:

  • First, create the ref const text = ref('').
  • Second, assign the ref to the v-model attribute: <input v-model="text" type="text" />

text ref is kept in sync (bound) with the input field value.

Do you have any questions about v-model?

Like the post? Please share!

Dmitri Pavlutin

About Dmitri Pavlutin

Software developer and sometimes writer. My daily routine consists of (but not limited to) drinking coffee, coding, writing, overcoming boredom 😉. Living in the sunny Barcelona. 🇪🇸