Vue Tip: Declare and Mutate v-model Props as Normal Variable Using defineModel


This experimental feature will be available in Vue 3.3. If you want to try it out now in Vue 2 or Vue 3, you can use it with the Vue Macros library.
defineModel
is a compiler macro that allows you to declare and mutate v-model
props as the same as a normal variable.
Example without defineModel
Let's take a look at a simple example that uses v-model
. We have a Parent.vue
component that passes a counter ref to a Child.vue
component:
<script setup lang="ts">
import { ref } from 'vue'
import Child from './components/Child.vue'
const state = ref({ count: 0 })
</script>
<template>
<div>
<span>Parent state: {{ state }}</span>
<Child v-model="state.count" />
</div>
</template>
Let's take a look at the implementation of the child component:
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps<{ modelValue: number }>()
const emit = defineEmits<{
(e: 'update:modelValue', value: number): void
}>()
const onClick = () => {
props.modelValue += 1
emit('update:modelValue', props.modelValue)
}
</script>
<template>
<div>
<span>Child state: {{ modelValue }}</span>
<button @click="onClick">Increment child state</button>
</div>
</template>
As Child.vue
receives the v-model
you need to declare a prop called modelValue
which receives the v-model
value. Additionally, you need to declare an emit called update:modelValue
that is used to update the parent that the modelValue
has been updated.
As props are readonly and you should not mutate them, you cannot update modelValue
and will receive the following warning:
Set operation on key "modelValue" failed: target is readonly.
I wrote a tip about this topic and how you can solve this warning. defineModel
provides a nice solution to this problem, so let's take a look at it.
Example with defineModel
In the following example, I'm using Vue Macros's defineModels which behaves identically to defineModel
available since Vue 3.3.0-alpha.9
We can simplify our child component by using defineModels
:
<script setup lang="ts">
const { modelValue, count } = defineModels<{
modelValue: number
}>()
const onClick = () => {
modelValue.value += 1
}
</script>
<template>
<div class="container">
<span>Child with defineModels state: {{ modelValue }}</span>
<button @click="onClick">Increment child state</button>
</div>
</template>
The defineModels
compiler macro will declare a prop with the same name and a corresponding update:propName
event when it is compiled.
By updating the modelValue
ref, the corresponding update:propName
event is automatically emitted.
Try it yourself in the following StackBlitz project:
If you liked this Vue tip, follow me on Twitter to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue newsletter: