Skip to main content

How does two-way binding (v-model) work?

1. What is v-model

v-model is a directive that binds a component variable's value to a form element (input, textarea, select, etc.) in both directions:

  • when the data changes → the UI updates
  • when the user types into the form → the variable updates

In other words, v-model = "data ↔ UI".


2. Basic usage example

javascript
<template> <input v-model="message" placeholder="Enter text" /> <p>You entered: {{ message }}</p> </template> <script setup> import { ref } from 'vue' const message = ref('') </script>

What happens:

  • When the user types text → message.value updates.
  • When message.value changes → the input automatically shows the new value.

Vue creates two-way synchronization without you having to write handlers by hand.


3. How v-model works "under the hood"

Take this code:

javascript
<input v-model="message" />

Vue translates it into plain attributes and events:

javascript
<input :value="message" @input="message = $event.target.value" />

In other words:

  • :value - one-way data binding (from the component → to the DOM);
  • @input - feedback (from the DOM → to the component).

That is how you get two-way data binding.


4. Working with different element types

ElementWhat it bindsExample
<input type="text">value ↔ text<input v-model="text" />
<input type="checkbox">checked ↔ boolean<input type="checkbox" v-model="isChecked" />
<input type="radio">checked ↔ value<input type="radio" value="A" v-model="choice" />
<select>value ↔ selected value<select v-model="option">
<textarea>value ↔ text<textarea v-model="comment"></textarea>

5. Two-way binding between components

v-model can be used not only with form elements, but also to link a parent and a child component.

Parent:

javascript
<UserInput v-model="username" />

Child component:

javascript
<template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template> <script setup> defineProps(['modelValue']) defineEmits(['update:modelValue']) </script>

The parent passes a value through v-model → the modelValue prop The child reports changes through the update:modelValue event

This is the standard v-model contract in Vue 3: prop: modelValueemit: update:modelValue


6. Several v-models on one component

Vue 3 lets you use several independent v-models with names:

javascript
<MyInput v-model:title="pageTitle" v-model:content="pageText" />

Child component:

javascript
<script setup> defineProps(['title', 'content']) defineEmits(['update:title', 'update:content']) </script>

7. v-model modifiers

Vue offers convenient modifiers that transform the data on input:

ModifierDescriptionExample
.lazyUpdates the value only after change, not input<input v-model.lazy="text" />
.trimAutomatically trims whitespace from the start and end<input v-model.trim="name" />
.numberConverts the value to a number<input v-model.number="age" />

8. Under the hood of reactivity

When you write v-model="x":

  1. Vue tracks that the DOM element depends on x;
  2. when x changes, the value attribute updates;
  3. on an input/change event, Vue triggers a reverse updatex = $event.target.value.

These two directions form a reactive loop, keeping the UI and the state instantly in sync.


Summary

v-model is the mechanism for two-way data binding in Vue.js.

It automatically synchronizes the component's state and the UI:

  • from code → to the DOM (:value="...")
  • from the DOM → to code (@input="...")

It works both with form elements and with components, and supports modifiers and multiple models.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.