Vue.js Component v-model
The v-model directive links an input to a data property so both stay in step. Custom components can offer the same two-way link. A parent writes v-model="rating" on a component tag, and the component reads and updates that value without extra wiring in the parent. Rating stars, toggle switches, date pickers, and search boxes all use this pattern.
Think of a thermostat on the wall. You read the current temperature from the display and turn a dial to change it. The display and the dial belong to one device, and the room setting stays in sync. A component with v-model behaves the same way: the parent owns the value, and the component displays it and requests changes.
What v-model Does Behind the Scenes
Vue expands v-model on a component into one prop and one event. The prop is named modelValue. The event is named update:modelValue. The component receives the prop for display and emits the event when the user changes something.
Diagram: Expanded v-model
Parent writes:
<StarRating v-model="score" />
Vue turns it into:
<StarRating
:modelValue="score"
@update:modelValue="score = $event" />
Parent score --(prop)--> Component shows the stars
Parent score <--(event)-- Component reports a new choiceBuild a Component with Props and Emits
The component below shows five stars. A click on a star emits the new number to the parent.
<div id="app">
<star-rating v-model="score"></star-rating>
<p>Your score: {{ score }}</p>
</div>
<script>
const app = Vue.createApp({
data() {
return { score: 3 };
}
});
app.component("star-rating", {
props: ["modelValue"],
emits: ["update:modelValue"],
template: `
<span>
<button v-for="n in 5" :key="n"
@click="$emit('update:modelValue', n)">
{{ n <= modelValue ? "★" : "☆" }}
</button>
</span>`
});
app.mount("#app");
</script>A click on the fourth star emits update:modelValue with the number 4. The parent stores 4 in score, passes it back down, and the component redraws four filled stars.
The defineModel Shortcut
Recent Vue 3 versions include defineModel() for components written with <script setup>. The macro creates the prop and the event together and returns a ref you can read and write like normal data.
<!-- ToggleSwitch.vue -->
<script setup>
const on = defineModel();
</script>
<template>
<button @click="on = !on">
{{ on ? "ON" : "OFF" }}
</button>
</template>
<!-- Parent.vue -->
<ToggleSwitch v-model="darkMode" />Assigning to on sends the new value to the parent. The macro removes the repeated prop and emit code.
Multiple v-model Bindings
One component can accept several linked values. Add an argument after v-model to name each one.
<UserName v-model:first="firstName" v-model:last="lastName" />
<!-- UserName.vue -->
<script setup>
const first = defineModel("first");
const last = defineModel("last");
</script>
<template>
<input v-model="first" placeholder="First name">
<input v-model="last" placeholder="Last name">
</template>Diagram: Two Named Bindings
Parent UserName component firstName <--> v-model:first <--> first input box lastName <--> v-model:last <--> last input box
Format Input with Modifiers
Built-in modifiers such as .trim and .number work on component bindings. Custom modifiers arrive in the component through a second value, which lets a component clean the text before it reaches the parent.
const [text, modifiers] = defineModel({
set(value) {
return modifiers.capitalize
? value.charAt(0).toUpperCase() + value.slice(1)
: value;
}
});A parent writes v-model.capitalize="title", and every update arrives with a capital first letter.
Common Mistakes
- Changing a
modelValueprop directly inside the child. Props are read-only, so emit an event instead. - Forgetting the
emitsdeclaration in Options API components. - Using a mismatched event name. The name must be exactly
update:modelValuefor the default binding.
Summary
Component v-model expands into the modelValue prop and the update:modelValue event. The parent owns the data, and the child displays it and requests changes. The defineModel() macro shortens the setup, named arguments allow several bindings on one component, and modifiers let a component adjust values before they travel upward.
