JAVASCRIPT
Customizing v-model for Reusable Components in Vue 3
Learn how to implement and customize `v-model` on your Vue 3 components, enabling two-way data binding and creating highly reusable, form-like elements.
// App.vue (Parent component)
<template>
<div>
<h1>Parent Component</h1>
<p>Message from custom input: {{ myMessage }}</p>
<p>Custom counter value: {{ myCounter }}</p>
<!-- Basic v-model usage for custom input -->
<CustomInput v-model="myMessage" label="Your Message" />
<!-- v-model with a custom argument for a counter -->
<CustomCounter v-model:count="myCounter" label="Counter" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import CustomInput from './components/CustomInput.vue';
import CustomCounter from './components/CustomCounter.vue';
const myMessage = ref('Hello Vue!');
const myCounter = ref(0);
</script>
// components/CustomInput.vue
<template>
<div style="border: 1px solid lightgray; padding: 10px; margin: 10px;">
<label>{{ label }}: </label>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
type="text"
/>
<p>Internal state: {{ modelValue }}</p>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
defineProps({
modelValue: String, // Default prop name for v-model
label: String,
});
defineEmits(['update:modelValue']); // Default event name for v-model
</script>
// components/CustomCounter.vue
<template>
<div style="border: 1px solid lightblue; padding: 10px; margin: 10px;">
<label>{{ label }}: </label>
<button @click="decrement">-</button>
<span> {{ count }} </span>
<button @click="increment">+</button>
<p>Internal state: {{ count }}</p>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
count: Number, // Custom prop name for v-model:count
label: String,
});
const emit = defineEmits(['update:count']); // Custom event name for v-model:count
const increment = () => {
emit('update:count', props.count + 1);
};
const decrement = () => {
emit('update:count', props.count - 1);
};
</script>
How it works: This snippet demonstrates how to implement `v-model` on custom components in Vue 3, enabling two-way data binding. The `CustomInput` component uses the default `modelValue` prop and `update:modelValue` event. `CustomCounter` shows how to customize `v-model` by providing an argument (e.g., `v-model:count`), which expects a `count` prop and emits an `update:count` event. This pattern makes custom components behave like native input elements, simplifying data synchronization between parent and child.