Component v-model
Basic Usage
Section titled “Basic Usage”v-model can be used on a component to implement a two-way binding.
Starting in Vue 3.4, the recommended approach to achieve this is using the defineModel() macro:
<script setup>const model = defineModel()
function update() {model.value++}</script>
<template><div>Parent bound v-model is: {{ model }}</div><button @click="update">Increment</button></template>The parent can then bind a value with v-model:
<Child v-model="countModel" />The value returned by defineModel() is a ref. It can be accessed and mutated like any other ref, except that it acts as a two-way binding between a parent value and a local one:
- Its .valueis synced with the value bound by the parentv-model;
- When it is mutated by the child, it causes the parent bound value to be updated as well.
This means you can also bind this ref to a native input element with v-model, making it straightforward to wrap native input elements while providing the same v-model usage:
<script setup>const model = defineModel()</script>
<template>  <input v-model="model" /></template>Try it in the playground
Under the Hood
Section titled “Under the Hood”defineModel is a convenience macro. The compiler expands it to the following:
- A prop named modelValue, which the local ref’s value is synced with;
- An event named update:modelValue, which is emitted when the local ref’s value is mutated.
This is how you would implement the same child component shown above prior to 3.4:
<script setup>const props = defineProps(['modelValue'])const emit = defineEmits(['update:modelValue'])</script>
<template>  <input    :value="props.modelValue"    @input="emit('update:modelValue', $event.target.value)"  /></template>Then, v-model="foo" in the parent component will be compiled to:
<Child  :modelValue="foo"  @update:modelValue="$event => (foo = $event)"/>As you can see, it is quite a bit more verbose. However, it is helpful to understand what is happening under the hood.
Because defineModel declares a prop, you can therefore declare the underlying prop’s options by passing it to defineModel:
// making the v-model requiredconst model = defineModel({ required: true })
// providing a default valueconst model = defineModel({ default: 0 })v-model Arguments
Section titled “v-model Arguments”v-model on a component can also accept an argument:
<MyComponent v-model:title="bookTitle" />In the child component, we can support the corresponding argument by passing a string to defineModel() as its first argument:
<script setup>const title = defineModel('title')</script>
<template>  <input type="text" v-model="title" /></template>Try it in the playground
If prop options are also needed, they should be passed after the model name:
const title = defineModel('title', { required: true })  <script setup>  defineProps({  title: {      required: true  }  })  defineEmits(['update:title'])  </script>
  <template>  <input      type="text"      :value="title"      @input="$emit('update:title', $event.target.value)"  />  </template>Try it in the playground
Multiple v-model Bindings
Section titled “Multiple v-model Bindings”By leveraging the ability to target a particular prop and event as we learned before with v-model arguments, we can now create multiple v-model bindings on a single component instance.
Each v-model will sync to a different prop, without the need for extra options in the component:
<UserName  v-model:first-name="first"  v-model:last-name="last"/><script setup>const firstName = defineModel('firstName')const lastName = defineModel('lastName')</script>
<template>  <input type="text" v-model="firstName" />  <input type="text" v-model="lastName" /></template>Try it in the playground
     <script setup>     defineProps({     firstName: String,     lastName: String     })
     defineEmits(['update:firstName', 'update:lastName'])     </script>
     <template>     <input         type="text"         :value="firstName"         @input="$emit('update:firstName', $event.target.value)"     />     <input         type="text"         :value="lastName"         @input="$emit('update:lastName', $event.target.value)"     />     </template>Try it in the playground
Handling v-model Modifiers
Section titled “Handling v-model Modifiers”When we were learning about form input bindings, we saw that v-model has built-in modifiers - .trim, .numberand.lazy. In some cases, you might also want the v-model` on your custom input component to support custom modifiers.
Let’s create an example custom modifier, capitalize, that capitalizes the first letter of the string provided by the v-model binding:
<MyComponent v-model.capitalize="myText" />Modifiers added to a component v-model can be accessed in the child component by destructuring the defineModel() return value like this:
<script setup>const [model, modifiers] = defineModel()
console.log(modifiers) // { capitalize: true }</script>
<template><input type="text" v-model="model" /></template>To conditionally adjust how the value should be read / written based on modifiers, we can pass get and set options to defineModel(). These two options receive the value on get / set of the model ref and should return a transformed value. This is how we can use the set option to implement the capitalize modifier:
<script setup>const [model, modifiers] = defineModel({  set(value) {    if (modifiers.capitalize) {      return value.charAt(0).toUpperCase() + value.slice(1)    }    return value  }})</script>
<template>  <input type="text" v-model="model" /></template>Try it in the playground
Modifiers for v-model with Arguments
Section titled “Modifiers for v-model with Arguments”Here’s another example of using modifiers with multiple v-model with different arguments:
<UserName  v-model:first-name.capitalize="first"  v-model:last-name.uppercase="last"/><script setup>const [firstName, firstNameModifiers] = defineModel('firstName')const [lastName, lastNameModifiers] = defineModel('lastName')
console.log(firstNameModifiers) // { capitalize: true }console.log(lastNameModifiers) // { uppercase: true }</script>- DirectoryPre 3.4 Usage- <script setup>const props = defineProps({firstName: String,lastName: String,firstNameModifiers: { default: () => ({}) },lastNameModifiers: { default: () => ({}) }})defineEmits(['update:firstName', 'update:lastName'])console.log(props.firstNameModifiers) // { capitalize: true }console.log(props.lastNameModifiers) // { uppercase: true }</script>