Lesson 8 / 25

v-model on Components

Two-way binding with defineModel.

A prop plus an update event

On a component, v-model="value" expands to a modelValue prop plus an update:modelValue event. Since Vue 3.4, the defineModel() macro declares both and returns a ref you can read and assign, which emits the update for you; check the docs for your version, since older projects write the prop and emit by hand. You can have several models with arguments, such as v-model:title, read with defineModel('title'), and built-in modifiers like .trim still work on native inputs inside.

A custom input with defineModel

Plus the manual equivalent for older versions.

<!-- SearchBox.vue (Vue 3.4+) -->
<script setup>
const model = defineModel({ type: String, default: '' })
</script>

<template>
  <input v-model="model" placeholder="Search..." />
</template>

<!-- Before 3.4: the same component written by hand
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
  <input :value="modelValue"
         @input="$emit('update:modelValue', $event.target.value)" />
</template>
-->

<!-- Parent: <SearchBox v-model="query" />  and  <UserForm v-model:title="title" /> -->

Keep the parent as the owner

Component v-model is still one-way flow underneath: the parent holds the state and the child only asks for updates through the event.

Quick check: What does v-model on a custom component expand to by default?

  • A shared global variable
  • A value prop and a change event on window
  • A modelValue prop and an update:modelValue event
  • A two-way Proxy between parent and child
Answer

A modelValue prop and an update:modelValue event — defineModel wraps this pair for you.