Updated June 2026. Tested on Vue 3.4 and later.

Setting up two way binding on a component used to be a manual chore. Props are read only, so a child could not change the value passed to it directly. To make v-model work on your own component you had to declare a modelValue prop and emit an update:modelValue event by hand. The defineModel macro, added in Vue 3.4, does all of that for you.

The old way

Before defineModel, a component that supported v-model looked like this. You define the prop, define the emit, and wire them together in the template.

<!-- MyInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>
<MyInput v-model="name" />

The new way

With defineModel the same component collapses to two lines that matter.

<!-- MyInput.vue -->
<script setup>
const model = defineModel()
</script>

<template>
  <input v-model="model" />
</template>
<MyInput v-model="name" />

What defineModel returns is a ref. Its .value stays in sync with the parent's v-model value, and when the child changes it, the parent updates too. No modelValue prop, no update:modelValue emit, no $event.target.value. The macro generates all of that under the hood.

Named and multiple models

If you want a name other than the default, or more than one v-model on a component, pass an identifier to defineModel.

<!-- NameFields.vue -->
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
  <input v-model="firstName" />
  <input v-model="lastName" />
</template>

Then bind each by name from the parent.

<script setup>
import { ref } from 'vue'

const first = ref('Akram')
const last = ref('Wahid')
</script>

<template>
  <NameFields v-model:first-name="first" v-model:last-name="last" />
</template>

Why it is worth using

Anyone who has kept several bindings in sync by hand knows how repetitive the prop and emit pairs get. defineModel removes that boilerplate, so your components are shorter and you stop worrying about whether you remembered to emit the right event. If you are on Vue 3.4 or later, use it. Questions and thoughts welcome in the comments.