Differences between using the V-Model on Vue 2 and 3
Posted on September 19, 2024 - 4 minutes of reading
The differences between using the v-model on Vue 2 and 3.
Index
Intro
This post is just meant to be an insight I had when migrating a piece of code to Vue's newest version and I ended up learning the difference existing when using the v-model.
Vue 3 has been around for a few years now (it was released in 2020), but as a Vue developer who used to work on Vue 2, and I believe still keep working because many projects created at least 2 years ago or more are likely to be still using this version.
Key points
- In Vue 2, only one
v-modelwas allowed per component, which was tightly bound to thevalueprop andinputevent. - If you needed multiple model-like bindings, you had to create custom props and events manually for each one.
- In Vue 3, you can now have multiple
v-modelbindings easily by using the formatv-model:<propName>. Eachv-modeluses theupdate:<propName>event convention, making it cleaner and more scalable than in Vue 2.
How it was in Vue 2
In Vue 2, the v-model directive worked differently compared to Vue 3. There was only one v-model per component, and it used a fixed prop/event pair (value and input by default).
- Prop: The default prop for
v-modelwas alwaysvalue. - Event: The corresponding event emitted from the child component was always
input.
For a single v-model, this was straightforward:
<template>
<input :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
props: ['value']
}
</script>When using v-model, it would bind to the value prop and listen for the input event.
<template>
<MyInput v-model="inputValue" />
</template>
<script>
export default {
data() {
return {
inputValue: ''
}
}
}
</script>Problem with multiple v-model in Vue 2
If you needed multiple v-model bindings for different values (e.g., name and age), Vue 2 did not support multiple v-model natively. You had to create custom prop names and custom events manually for each prop.
Example of multiple "v-model-like" bindings in Vue 2
<template>
<div>
<input :value="name" @input="$emit('update:name', $event.target.value)">
<input :value="age" @input="$emit('update:age', $event.target.value)">
</div>
</template>
<script>
export default {
props: ['name', 'age']
}
</script>In the parent component:
<template>
<MyComponent
:name="userName"
:age="userAge"
@update:name="userName = $event"
@update:age="userAge = $event"
/>
</template>
<script>
export default {
data() {
return {
userName: '',
userAge: ''
}
}
}
</script>How v-model works in Vue 3
- Prop: Vue 3 uses
modelValueas the prop that stores the data when you usev-modelin the parent component. - Event: The corresponding event emitted from the child component should be named
update:modelValue.
So, when you use v-model="value" (here "value" could be any variable name within your SFC) in the parent, Vue automatically binds modelValue as the prop in the child and listens for an event named update:modelValue. This event is how Vue knows the model value has changed and updates the parent data accordingly.
Why "update:"?
The use of update is a naming convention that clearly indicates the intent to update the modelValue. It makes Vue's code more explicit by:
- Automatically tying together the prop and its event without custom event naming.
- Allowing for multiple
v-modelbindings, where you can bind other properties (e.g.,v-model:checked,v-model:content), each following the sameupdate:<propName>event convention.
This change from Vue 2 simplifies things, especially when you need more than one v-model in a single component.
How to use multiple v-model in Vue 3
You can specify multiple v-model bindings by using the syntax v-model:<propName>. Each v-model is tied to a specific prop and an event in the child component. For each prop, Vue expects an event named update:<propName>.
- In the Child Component: Emit
update:<propName>events for each prop that should be bound withv-model. - In the Parent Component: Use
v-model:<propName>for each prop you want to bind, ensuring clear and clean syntax.
This structure makes it much easier to handle multiple v-model bindings in Vue 3, improving both flexibility and readability!
Example of multiple "v-model-like" bindings in Vue 3
In the child component, you bind the props name and age to the respective input fields value.
And emit events update:name and update:age to update the parent's userName and userAge.
<template>
<div>
<input
type="text"
placeholder="Enter your name"
:value="name"
@input="$emit('update:name', $event.target.value)"
/>
<input
type="number"
placeholder="Enter your age"
:value="age"
@input="$emit('update:age', $event.target.value)"
/>
</div>
</template>
<script setup lang="ts">
defineProps<{
name: string
age: number
}>()
</script>In the parent, you use v-model:name for userName and v-model:age for userAge, which makes it easy to bind multiple properties at once.
<template>
<MyComponent v-model:name="userName" v-model:age="userAge" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const userName = ref('')
const userAge = ref(0)
</script>References
- https://vuejs.org/guide/components/v-model.html
- https://v3-migration.vuejs.org/breaking-changes/v-model.html
Conclusion
Sometimes we come across a different pattern in Vue 3 and it's good to know why it changed and how it works now. You can refer to the official docs created to help developers migrating from Vue 2 here.
What did you think of this post? Do you have any questions, suggestions or criticisms? Leave a reaction or a comment below. Thanks for visiting! 😉

