How To Add v-model Support to Custom Vue.js Components



The v-model directive is one of the few directives that comes bundled with Vue.js. This directive allows for two-way data binding between our data and views.

With two-way data binding, when we update our data via input fields or other controls, we can modify the DOM (Document Object Model) without having to do DOM work.

In this article, you’ll explore how this directive works and use it for your own components.

Understanding How v-model Works Internally

From our knowledge of HTML, we know that input, select, and textarea are the main ways we feed data to our application.

For v-model to work, it expects the element or component in question to receive a prop (default is value) and also emit an event (default is input).

Depending on the element, Vue decides how to listen and handle the data. For input elements, you might use v-model like this:

<input v-model="email" />

v-model translates to this:

<input :value="email" @input="e => email = e.target.value" />

Vue uses this expansion to handle textarea, select, and some other input types.

For radio buttons and checkboxes, Vue uses their checked prop and listens for their change event.

For elements like select tags and checkboxes that can accept multiple values, Vue will automatically return an array of selected values.

Adding v-model to Custom Components

To let our component support v-model two-way binding, the component needs to accept a value prop and emit an input event.

Let’s create a sample component called basic-input. We’ll use Vue’s single file component:

  <input @input="handleInput" />

export default {
  prop: ['value'],
  data () {
    return {
      content: this.value
  methods: {
    handleInput (e) {
      this.$emit('input', this.content)

To support v-model, the component accepts a value prop and emits an input event.

Use the component like this:

<basic-input v-model="email" />

With that, the custom component supports v-model two-way binding.

Customizing v-model prop and event

Let’s take it a step further. We might not want to use the default event and prop needed to add v-model support to our components. Thankfully, Vue allows us to customize it.

To customize the event and `prop, we add amodel` property to our component and define the new values like this:

// ...

export default {
  prop: ['hidden'],
  model: {
      prop: 'hidden',
      event: 'blur'
  methods: {
      handleInput (value) {
          this.$emit('blur', value)

// ...

This time, when you use the component like this:

<basic-input v-model="email" />

Vue will automatically convert it into:

<basic-input :hidden="email" @blur="e => email = e.target.value" />

With this in place, you can avoid conflicts when defining your component’s props and events.

Using v-model on contenteditable

A content editable element is a div or similar element that can be configured to work as an input.

We define content editable elements by adding the contenteditable attribute to the element:

<div class="editor" contenteditable="contenteditable"></div>

You’ll use content editable elements for WYSIWYG editors as they are easier to work with and are supported by a large amount of browsers.

v-model will work on content editable elements, but you need to explicitly use the content of the element, or the content will not be emitted.

To emit the content, you need to grab the innerText or innerHTML of the div. So, our updateInput method needs to look like this:

updateInput () {
  this.$emit('input', this.$el.innerText)

You can also use the content of a ref instead of the root element’s content.

With this in place, v-model will work for content editable elements. You could also update this.content in the updateInput method.


Now that you have seen how to use v-model with custom Vue components, you can go build or refactor your components that require the use of v-model.

For further reading, consult the official documentation for v-model or check out the Vue.js topic page for more exercises or projects.

Creative Commons License