iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

v-model

v-model is two-way binding sugar. On native inputs it reads value + listens for input; on components it reads the modelValue prop + emits update:modelValue.

Native, modifiers, custom components, multi

EXAMPLE
<script setup>
import { ref, computed } from 'vue';

const email   = ref('');
const age     = ref(0);
const checked = ref(false);
const color   = ref('blue');
const tags    = ref(['a', 'b']);
const comment = ref('');
</script>

<template>
    <!-- 1) Native inputs — v-model uses value + input -->
    <input v-model="email" type="email" placeholder="Email" />
    <input v-model.number="age" type="number" />
    <input v-model.trim="comment" />
    <input v-model.lazy="comment" />     <!-- updates on change instead of input -->

    <!-- 2) Checkbox — boolean OR array -->
    <input type="checkbox" v-model="checked" /> {{ checked }}

    <!-- Multiple checkboxes binding to an array -->
    <input type="checkbox" v-model="tags" value="a" /> A
    <input type="checkbox" v-model="tags" value="b" /> B
    <input type="checkbox" v-model="tags" value="c" /> C
    <p>{{ tags }}</p>

    <!-- 3) Radio -->
    <input type="radio" v-model="color" value="red" /> Red
    <input type="radio" v-model="color" value="blue" /> Blue
    <input type="radio" v-model="color" value="green" /> Green

    <!-- 4) Select -->
    <select v-model="color">
        <option value="red">Red</option>
        <option value="blue">Blue</option>
        <option value="green">Green</option>
    </select>

    <!-- Multi-select -->
    <select v-model="tags" multiple>
        <option value="a">A</option>
        <option value="b">B</option>
        <option value="c">C</option>
    </select>

    <!-- 5) Textarea -->
    <textarea v-model="comment" rows="4"></textarea>
</template>

<!-- 6) Custom component as a v-model target — needs modelValue prop + update:modelValue event -->
<!-- MoneyInput.vue -->
<script setup>
import { computed } from 'vue';
const props = defineProps({ modelValue: { type: Number, default: 0 } });
const emit  = defineEmits(['update:modelValue']);

const display = computed({
    get: () => props.modelValue.toFixed(2),
    set: (v) => emit('update:modelValue', parseFloat(v) || 0),
});
</script>

<template>
    <span>$</span>
    <input :value="display" @@input="display = $event.target.value" inputmode="decimal" />
</template>

<!-- Usage -->
<MoneyInput v-model="price" />

<!-- 7) Multiple v-models — named -->
<!-- AddressInput.vue -->
<script setup>
defineProps(['street', 'city', 'postcode']);
defineEmits(['update:street', 'update:city', 'update:postcode']);
</script>
<template>
    <input :value="street"   @@input="$emit('update:street',   $event.target.value)" placeholder="Street" />
    <input :value="city"     @@input="$emit('update:city',     $event.target.value)" placeholder="City" />
    <input :value="postcode" @@input="$emit('update:postcode', $event.target.value)" placeholder="Postcode" />
</template>

<!-- Parent -->
<AddressInput v-model:street="a.street" v-model:city="a.city" v-model:postcode="a.postcode" />

<!-- 8) Custom modifier — handle .trim / .number / your own -->
<script setup>
const props = defineProps({
    modelValue: String,
    modelModifiers: { default: () => ({}) },
});
const emit  = defineEmits(['update:modelValue']);

function emitVal(value) {
    if (props.modelModifiers.uppercase) value = value.toUpperCase();
    emit('update:modelValue', value);
}
</script>

<!-- Parent -->
<UppercaseInput v-model.uppercase="name" />

<!-- 9) Best practices -->
<!-- - Always validate at the destination (form submit), not just via the binding -->
<!-- - For complex forms, reach for vee-validate / formkit instead of hand-rolled v-model -->
<!-- - Don't fight v-model — if you need extra logic, use :value / @input directly -->

Why it matters

v-model is just :modelValue + @update:modelValue with a sweet syntax. Knowing the underlying contract makes custom inputs (and multi-v-model components) trivial.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
<input v-model="name">
<p>Hello, {{ name }}!</p>
Try it Yourself »

Exercise

Two-way bind an input to data.

<input "name">

Test yourself

Q1. v-model is most commonly used on…
Q2. v-model is syntax sugar for…
Q3. On custom components, v-model uses the prop/event named…

Discussion

Loading…