.lazy, .number, .trim — Convenience Built In
v-model Modifiers
v-model accepts modifiers that tweak when and how the ref is updated: .lazy, .number, and .trim cover the common cases.
What you'll learn
- Use .lazy to sync on change instead of input
- Use .number to coerce the value to a number
- Use .trim to strip whitespace
v-model exposes three built-in modifiers that handle small but common chores: timing, type coercion, and whitespace trimming.
.lazy
By default v-model listens to the input event and fires on every keystroke. The .lazy modifier switches it to the change event, which fires when the input loses focus.
<script setup lang="ts">
import { ref } from 'vue'
const msg = ref('')
</script>
<template>
<input v-model.lazy="msg" />
<p>Synced on blur: {{ msg }}</p>
</template> Use .lazy when running expensive logic in a watcher (search, validation) and you do not want it firing on every keystroke.
.number
User input is always a string. .number runs parseFloat on the value before storing it. If parsing fails the original string is kept, so combine it with a number input or numeric validation.
<script setup lang="ts">
import { ref } from 'vue'
const age = ref<number | null>(null)
</script>
<template>
<input type="number" v-model.number="age" />
<p>Type: {{ typeof age }} — Value: {{ age }}</p>
</template> .trim
.trim calls String.prototype.trim on the value before storing. Useful for emails, usernames, and anything where leading or trailing spaces are accidents.
<input v-model.trim="email" /> Chaining
Modifiers stack. Order does not change the outcome for the built-ins, but the convention is to write them in the order they apply.
<input v-model.trim.lazy="name" />
<input type="number" v-model.number.lazy="quantity" /> Custom v-model on a component can also expose its own modifiers — you receive them as a second argument to defineModel.
Custom v-model on Components →