Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Forms Handling  ›  Capstone Project

v-model on Your Own Components

v-model 14 mindefineModel lets a child component support v-model, returning a ref that reads the parent's value and writes changes back.

v-model on Your Own Components

Shelf's Add Book form needs a star rating. You build a StarRating component with five clickable stars, and you want to use it exactly like an input: <StarRating v-model="rating" />. For that, the component must follow the v-model contract. Since Vue 3.4, defineModel does the wiring in one line.

The rule

On a component, v-model is a modelValue prop plus an update:modelValue event; defineModel handles both.

The child:

<!-- StarRating.vue -->
<script setup>
const rating = defineModel({ type: Number, default: 0 })
</script>

<template>
  <div class="stars">
    <button v-for="n in 5" :key="n" type="button"
            :class="{ filled: n <= rating }"
            @click="rating = n">★</button>
  </div>
</template>

The parent:

<script setup>
import { ref } from 'vue'
import StarRating from './StarRating.vue'
const rating = ref(3)
</script>

<template>
  <StarRating v-model="rating" />
  <p>You gave it {{ rating }} stars.</p>
</template>

What happens underneath: on a component, v-model="rating" expands to

<StarRating :modelValue="rating" @update:modelValue="v => rating = v" />

defineModel() declares that prop and that event for you and returns a ref. Reading it gives the parent's current value. Assigning to it, as rating = n in the template or rating.value = n in script, emits update:modelValue with the new value, and the parent's ref updates. The child never mutates a prop; it asks, and the parent's v-model listener makes the change. This is still "props down, events up", just packaged.

Several models. Give the model a name to bind more than one value: defineModel('title') in the child pairs with v-model:title="book.title" in the parent. A component can have v-model:title and v-model:rating side by side.

Before Vue 3.4, you wrote the contract by hand: defineProps(['modelValue']) plus defineEmits(['update:modelValue']), and emit('update:modelValue', n) on click. You will still see this in older code, and it behaves the same.

The shortcut

Picture the expansion every time: v-model="x" on a component means "pass x as modelValue, and when the child says update:modelValue, write the value into x". With a name, swap modelValue for that name.

Clicking the fourth starStep 1 of 4
  1. Parent passes down

    rating is 3, so StarRating receives modelValue 3. Three stars are filled.

Drills

Answer fast — trust the rule.

quick drill

On a component, v-model="rating" expands to which prop and event?

quick drill

Inside StarRating, const rating = defineModel(). What does rating.value = 5 do?

quick drill

The parent writes <BookForm v-model:title="book.title" />. What does the child use?

quick drill

Which pre-3.4 code is equivalent to const rating = defineModel()?

Remember

  • Component v-model = modelValue prop + update:modelValue event; defineModel() gives you a ref that does both.
  • Shortcut: picture the expansion; a named model swaps modelValue for the name.
  • Trap: mutating a plain prop instead of using a model; the parent must make the change.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainA StarRating component

Build a StarRating component that works as <StarRating v-model="book.rating" />. Write its key code and explain what v-model expands to between parent and child.