Apps Artificial Intelligence CSS DevOps Go JavaScript Laravel Linux MongoDB MySQL PHP Python Rust Svelte Vue

Two-Way Binding with `v-model` in Vue 3

2 min read .
Two-Way Binding with `v-model` in Vue 3

Two-way binding is a common part of interactive Vue applications. In Vue 3, v-model provides a concise way to synchronize form controls with component state. This guide covers the basic syntax, common input types, custom components, and a few practical guidelines.

1. What Is Two-Way Binding?

Two-way binding keeps a UI control and JavaScript state synchronized. When the user changes an input, the state updates; when the state changes, the rendered input reflects the new value.

2. Basic v-model Syntax

<template>
  <input v-model="message" placeholder="Type something" />
  <p>Message: {{ message }}</p>
</template>

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

const message = ref('');
</script>

The text input and message remain synchronized automatically.

3. Bind Different Input Types

v-model works with text fields, checkboxes, radio controls, and selects:

<template>
  <input type="text" v-model="textInput" placeholder="Text input" />
  <input type="checkbox" v-model="isChecked" /> Checked: {{ isChecked }}
  <select v-model="selected">
    <option disabled value="">Please select one</option>
    <option>Option 1</option>
    <option>Option 2</option>
  </select>
</template>

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

const textInput = ref('');
const isChecked = ref(false);
const selected = ref('');
</script>

Vue applies the appropriate value/checked behavior for each control type.

4. Use v-model with a Custom Component

For a custom component, the conventional Vue 3 contract uses a modelValue prop and an update:modelValue event:

<!-- CustomInput.vue -->
<template>
  <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>

<script setup>
defineProps(['modelValue']);
defineEmits(['update:modelValue']);
</script>
<!-- ParentComponent.vue -->
<template>
  <CustomInput v-model="customInputValue" />
</template>

<script setup>
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';

const customInputValue = ref('');
</script>

When the child emits update:modelValue, Vue updates customInputValue in the parent.

5. Practical Uses

v-model is especially useful for:

  • Form fields that need immediate state synchronization.
  • Search or filter controls.
  • Checkboxes, toggles, and selections that directly represent component state.
  • Reusable form components that expose a standard two-way binding API.

6. Common Mistakes

  • Forgetting the modelValue / update:modelValue contract in custom components.
  • Mutating props directly instead of emitting an update.
  • Using two-way binding when a one-way prop plus an explicit event would make the data flow clearer.

7. Best Practices

  • Use local state for data that belongs only to one component.
  • Keep custom component v-model contracts explicit and predictable.
  • Prefer clear one-way data flow when automatic synchronization is not actually needed.
  • In modern Vue 3 versions, defineModel() can reduce boilerplate for custom component models when your project supports it.

Conclusion

v-model is one of Vue 3’s core tools for building forms and interactive components. Understanding how it maps to component state and the modelValue event contract makes it easier to design reusable, maintainable component APIs.

Related Posts

chevron-up