Event Handling

This section documents the current Vue 3.x release line as published at the official Vue.js documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. The Composition API with <script setup> is the authoring style used throughout; the Options API is shown only as a contrast.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vue and its ecosystem iterate quickly.

This section’s bibliography lists the reference material consulted while preparing these pages.

Vue listens to DOM events with the v-on directive, almost always written with its @ shorthand. The handler is either an inline expression or a method, and a chain of modifiers absorbs the boilerplate that normally clutters an event callback. The reference for everything on this page is Event Handling.

v-on and the @ shorthand

@click="handler" is shorthand for v-on:click="handler". The name after the colon (or @) is the native DOM event type — click, input, submit, keyup, pointerdown, and so on. Components can also emit their own events; that case is in Component Events and v-model.

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <button @click="count++">Add one</button>
  <p>Clicked {{ count }} times</p>
</template>

Inline vs. method handlers

An inline handler is a JavaScript statement — good for something trivial. A method handler is the name of a function (or a property path); Vue calls it with the native event as the only argument. See Method Handlers and Inline Handlers.

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment(event) {
  count.value++
  console.log(event.currentTarget.tagName) // "BUTTON"
}
</script>

<template>
  <!-- inline: a statement -->
  <button @click="count++">inline</button>

  <!-- method: receives the native event -->
  <button @click="increment">method</button>
</template>

Reaching the native event

In an inline handler the native event is available as the special $event variable; alternatively write an arrow function and name the parameter. Both let you pass extra arguments alongside the event. See Accessing Event Argument in Inline Handlers.

<template>
  <!-- $event -->
  <input @input="text = $event.target.value">

  <!-- arrow function -->
  <input @input="(e) => text = e.target.value">

  <!-- a value plus the event -->
  <button @click="save('draft', $event)">Save</button>
</template>

Event modifiers

Modifiers appended to the event handle the common DOM calls for you, so the method stays about logic. The full set is .stop, .prevent, .self, .capture, .once, and .passive; see Event Modifiers.

<template>
  <!-- event.stopPropagation() -->
  <a @click.stop="onClick">stop</a>

  <!-- event.preventDefault() -->
  <form @submit.prevent="onSubmit">prevent</form>

  <!-- chained: both, in order -->
  <a @click.stop.prevent="onClick">both</a>

  <!-- only when event.target is this element, not a descendant -->
  <div @click.self="onClick">self</div>

  <!-- listener attached in the capture phase -->
  <div @click.capture="onClick">capture</div>

  <!-- fires at most once, then the listener is removed -->
  <button @click.once="onClick">once</button>

  <!-- addEventListener(..., { passive: true }); cannot call preventDefault() -->
  <div @scroll.passive="onScroll">passive</div>
</template>

Modifiers are order-sensitive because the generated code runs in the order written: @click.prevent.self prevents the default on the element and its children, while @click.self.prevent only prevents it when the click is on the element itself.

.passive and .prevent contradict each other — .prevent is ignored on a passive listener and the browser logs a warning. Use .passive for touch and scroll listeners where you never need to cancel the default.

Key modifiers

For keyboard events, a modifier restricts the handler to a key. .enter, .tab, .delete (Delete and Backspace), .esc, .space, .up, .down, .left, and .right are named aliases; any other KeyboardEvent.key value works in kebab-case (@keyup.page-down, @keyup.arrow-up). See Key Modifiers.

<template>
  <input @keyup.enter="submit">
  <input @keyup.esc="clear">
  <input @keyup.page-down="onPageDown">
</template>

System modifier keys

.ctrl, .alt, .shift, and .meta require that modifier key to be held when the event fires. They pair with a key event or a mouse event, and unlike the key aliases they match even if other keys are also down. See System Modifier Keys.

<template>
  <input @keyup.alt.enter="send">
  <div @click.ctrl="onCtrlClick">Ctrl-click me</div>
</template>

.exact

.exact pins down which system modifiers may be pressed — no more, no less. See the .exact modifier.

<template>
  <!-- also fires when Alt or Shift is held too -->
  <button @click.ctrl="onClick">A</button>

  <!-- fires only when Ctrl and no other system modifier is held -->
  <button @click.ctrl.exact="onClick">B</button>

  <!-- fires only when no system modifier at all is held -->
  <button @click.exact="onClick">C</button>
</template>

Mouse-button modifiers

.left, .right, and .middle restrict a mouse handler to a single button. See Mouse Button Modifiers.

<template>
  <button @mousedown.middle="paste">middle-click paste</button>
  <div @click.right="onContextMenu">right-click</div>
</template>

See also