# Custom Events

This page assumes you've already read the Components Basics. Read that first if you are new to components.

# Event Names

Unlike components and props, event names don't provide any automatic case transformation. Instead, the name of an emitted event must exactly match the name used to listen to that event.

this.$emit('my-event')
1
<my-component @my-event="doSomething"></my-component>
1

If we're emitting a camelCased event name:

this.$emit('myEvent')
1

Listening to the kebab-cased version will have no effect:

<!-- Won't work -->
<my-component @my-event="doSomething"></my-component>
1
2

Since event names will never be used as variable or property names in JavaScript, there is no reason to use camelCase or PascalCase. Additionally, v-on event listeners inside DOM templates will be automatically transformed to lowercase (due to HTML's case-insensitivity), so @myEvent would become @myevent -- making myEvent impossible to listen to.

For these reasons, we recommend you always use kebab-case for event names.

# Defining Custom Events

Watch a free video about Defining Custom Events on Vue School

Emitted events can be defined on the component via the emits option.

app.component('custom-form', {
  emits: ['in-focus', 'submit']
})
1
2
3

When a native event (e.g., click) is defined in the emits option, the component event will be used instead of a native event listener.

TIP

It is recommended to define all emitted events in order to better document how a component should work.

# Validate Emitted Events

Similar to prop type validation, an emitted event can be validated if it is defined with the Object syntax instead of the Array syntax.

To add validation, the event is assigned a function that receives the arguments passed to the $emit call and returns a boolean to indicate whether the event is valid or not.

app.component('custom-form', {
  emits: {
    // No validation
    click: null,

    // Validate submit event
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm() {
      this.$emit('submit', { email, password })
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# v-model arguments

By default, v-model on a component uses modelValue as the prop and update:modelValue as the event. We can modify these names passing an argument to v-model:

<my-component v-model:title="bookTitle"></my-component>
1

In this case, child component will expect a title prop and emits update:title event to sync:

const app = Vue.createApp({})

app.component('my-component', {
  props: {
    title: String
  },
  template: `
    <input 
      type="text"
      :value="title"
      @input="$emit('update:title', $event.target.value)">
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
<my-component v-model:title="bookTitle"></my-component>
1

# Multiple v-model bindings

By leveraging the ability to target a particular prop and event as we learned before with v-model arguments, we can now create multiple v-model bindings on a single component instance.

Each v-model will sync to a different prop, without the need for extra options in the component:

<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>
1
2
3
4
const app = Vue.createApp({})

app.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  template: `
    <input 
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)">

    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)">
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

See the Pen Multiple v-models by Vue (@Vue) on CodePen.

# Handling v-model modifiers

When we were learning about form input bindings, we saw that v-model has built-in modifiers - .trim, .number and .lazy. In some cases, however, you might also want to add your own custom modifiers.

Let's create an example custom modifier, capitalize, that capitalizes the first letter of the string provided by the v-model binding.

Modifiers added to a component v-model will be provided to the component via the modelModifiers prop. In the below example, we have created a component that contains a modelModifiers prop that defaults to an empty object.

Notice that when the component's created lifecycle hook triggers, the modelModifiers prop contains capitalize and its value is true - due to it being set on the v-model binding v-model.capitalize="bar".

<my-component v-model.capitalize="bar"></my-component>
1
app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  template: `
    <input type="text" 
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)">
  `,
  created() {
    console.log(this.modelModifiers) // { capitalize: true }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Now that we have our prop set up, we can check the modelModifiers object keys and write a handler to change the emitted value. In the code below we will capitalize the string whenever the <input /> element fires an input event.

<div id="app">
  <my-component v-model.capitalize="myText"></my-component>
  {{ myText }}
</div>
1
2
3
4
const app = Vue.createApp({
  data() {
    return {
      myText: ''
    }
  }
})

app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  },
  template: `<input
    type="text"
    :value="modelValue"
    @input="emitValue">`
})

app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

For v-model bindings with arguments, the generated prop name will be arg + "Modifiers":

<my-component v-model:foo.capitalize="bar"></my-component>
1
app.component('my-component', {
  props: ['foo', 'fooModifiers'],
  template: `
    <input type="text" 
      :value="foo"
      @input="$emit('update:foo', $event.target.value)">
  `,
  created() {
    console.log(this.fooModifiers) // { capitalize: true }
  }
})
1
2
3
4
5
6
7
8
9
10
11

Deployed on Netlify.
Last updated: 10/27/2020, 7:18:08 PM