JAVASCRIPT

Building Custom `v-model` Components in Vue 3 with `modelValue` and `update:modelValue`

Extend Vue 3's `v-model` functionality to your custom components using `modelValue` prop and `update:modelValue` event for two-way data binding.

// CustomInput.vue
<template>
  <div class="custom-input-wrapper">
    <label for="custom-field">{{ label }}:</label>
    <input
      type="text"
      :id="id"
      :value="modelValue"
      @input="handleInput"
      class="custom-input"
      :placeholder="placeholder"
    />
  </div>
</template>

<script setup>
import { defineProps, defineEmits, computed } from 'vue';

const props = defineProps({
  modelValue: String, // The prop name MUST be 'modelValue'
  label: {
    type: String,
    default: 'Field',
  },
  placeholder: {
    type: String,
    default: '',
  },
  id: {
    type: String,
    default: () => `custom-input-${Math.random().toString(36).substr(2, 9)}`,
  },
});

// The event name MUST be 'update:modelValue'
const emit = defineEmits(['update:modelValue']);

function handleInput(event) {
  emit('update:modelValue', event.target.value);
}
</script>

<style scoped>
.custom-input-wrapper {
  margin-bottom: 15px;
}
.custom-input {
  padding: 8px 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 100%;
  max-width: 300px;
  box-sizing: border-box;
}
.custom-input:focus {
  outline: none;
  border-color: #42b983;
  box-shadow: 0 0 0 3px rgba(66, 185, 131, 0.2);
}
label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}
</style>

// App.vue (Parent component usage)
<template>
  <div>
    <h1>Custom v-model Example</h1>
    
    <CustomInput
      v-model="userName"
      label="Your Name"
      placeholder="Enter your name"
    />
    <p>User Name: <strong>{{ userName }}</strong></p>

    <CustomInput
      v-model="userEmail"
      label="Your Email"
      id="email-field"
    />
    <p>User Email: <strong>{{ userEmail }}</strong></p>
  </div>
</template>

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

const userName = ref('John Doe');
const userEmail = ref('');
</script>
How it works: Vue 3 allows you to implement `v-model` on custom components, making them behave like native form inputs with two-way data binding. The convention requires a `modelValue` prop to receive the value from the parent and an `update:modelValue` event to emit changes back. In `CustomInput.vue`, the component accepts `modelValue` as a prop and emits `update:modelValue` whenever its internal input changes. This setup enables the parent component (`App.vue`) to use `v-model="userName"` directly, creating a clean and familiar syntax for complex custom form controls, improving reusability and developer experience.

Need help integrating this into your project?

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

Hire DigitalCodeLabs