← Back to all snippets
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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs