modelValue and update:modelValue
Everyone new to Vue loves v-model. Binding a value to an input couldn’t be easier. But if you don’t understand what happens behind the scenes, v-model won’t work on your own components—and the magic breaks. 🙃
In this post we’ll explain how v-model works step by step, and what modelValue and update:modelValue actually represent.
v-model in a Nutshell
At its simplest:<input v-model="name" />
Vue compiles this to:<input :value="name" @input="name = $event.target.value" />
So v-model is shorthand for a :value binding plus an @input listener. But that’s only for native HTML inputs. Things change with custom components. 👇How Does v-model Work on Components?
When you usev-model on your own component, Vue automatically looks for two things:- A prop named
modelValue, - An emit named
update:modelValue.
<!-- Parent -->
<MyInput v-model="username" />
<!-- MyInput.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input :value="modelValue" @input="emit('update:modelValue', $event.target.value)" />
</template>
Now v-model behaves just like on a native input: the parent sends the value (via props), the child reports changes (via emit).
What’s Happening Internally?
v-model is a data-flow protocol with two directions:- Downstream (parent → child): value flows through the
modelValueprop. - Upstream (child → parent): changes are reported via the
update:modelValueevent.
username reactive, and that state is automatically bound to the child input’s value.In short: v-model turns “props in, emits out” into a two-way contract.
Renaming Is Possible
Sometimes you want multiplev-models (e.g., one for value, one for checked). Vue supports that too:<MyToggle v-model:checked="isOn" v-model:label="title" />
<script setup>
const props = defineProps(['checked', 'label'])
const emit = defineEmits(['update:checked', 'update:label'])
</script>
Each v-model pairs with its own prop/event duo.💡 Tip: Multiple v-models are great for complex form components—e.g., controlling both “value” and “valid” state.
What Does the Compiler Actually Generate?
When the template compiler seesv-model, it translates it to this pattern:<MyInput :modelValue="username" @update:modelValue="username = $event" />
No extra code needed. But if your component doesn’t declare defineEmits(['update:modelValue']), Vue will warn: > Missing required emit event: update:modelValueSo this convention is what makes v-model work.
Advanced: Computed v-model
Sometimes you want to transform the parent’s value inside the component. Use a computed getter/setter:const message = ref('Hello')
const upper = computed({
get: () => message.value.toUpperCase(),
set: val => message.value = val.toLowerCase()
})
With v-model="upper", the input always displays uppercase but stores lowercase. 😎v-model Modifiers
Vue supports modifiers like.trim, .number, .lazy:<input v-model.trim.number="age" />
These are tiny transforms around the input event—v-model.number is like applying parseFloat($event.target.value)..lazy hooks into change instead of input, useful for performance or validation.
When to Use Multiple v-models?
To manage multiple states (e.g.,value, error, valid) in form components:<MyField v-model:value="text" v-model:valid="isValid" />
This pattern is common in validation libraries—each pair represents an independent data stream.Real-World Example
A simple modal component:<Modal v-model:open="isOpen" />
Child:<script setup>
const props = defineProps(['open'])
const emit = defineEmits(['update:open'])
function close(){ emit('update:open', false) }
</script>
When isOpen = false in the parent, the modal closes; when the modal closes itself, the parent updates too. Two-way control complete.Common Pitfalls
- Forgetting
defineEmits(['update:modelValue']). - Manually writing both
:modelValueand@update:modelValuewherev-modelwould suffice. - Trying to mutate a prop (
props.modelValue = 'x') → readonly error. - Destructuring props (
const { modelValue } = props) can break reactivity when used carelessly.
Summary Table
| Term | Meaning |
|---|---|
v-model | Two-way binding shorthand |
modelValue | Data flowing in from the parent |
update:modelValue | Event flowing out from the child |
v-model:custom | Multiple model support |
v-model.trim | Trims input text |
v-model.lazy | Listens on change instead of input |
Conclusion
v-model is an elegant data-exchange protocol in Vue’s reactivity architecture. It might look like just a prop/emit pair, but it standardizes component communication in a powerful way.In short:
modelValue→ data inupdate:modelValue→ data outv-model→ binds the two automatically
Once you internalize this, any component can behave like a “form element,” and complex data flows become effortless.