AcademyVue
Event Handling: v-on and Modifiers
Passing a handler versus calling it in the template is the same pitfall React has with onClick, and modifiers replace the boilerplate everyone hand-writes for it.
2 min readUpdated 3 exercises
A bare method call runs at render, not at the event
v-on:click="fn" (shorthand @click="fn") passes the function itself, to be called when the event fires. Writing @click="fn()" calls it immediately, during template evaluation, and passes whatever it returns — usually undefined — as the handler instead:
<template>
<!-- Bug: addTodo() runs once, on render, not on click -->
<button @click="addTodo()">Add</button>
<!-- Correct: the reference is passed -->
<button @click="addTodo">Add</button>
<!-- Correct, and the only way to pass an argument -->
<button @click="() => removeTodo(index)">Remove</button>
</template>The rule is the same one a React codebase enforces on onClick: pass a reference for a no-argument call, wrap in an arrow function the moment you need to pass something the handler does not receive from the event itself.
Modifiers replace the boilerplate every handler used to write by hand
Before modifiers existed, every form handler opened with the same two lines:
function handleSubmit(event) {
event.preventDefault()
// ...
}.prevent moves that call out of the handler and onto the directive itself:
<template>
<form @submit.prevent="handleSubmit">
<input v-model="draft" />
<button type="submit">Add</button>
</form>
</template>Without it, pressing Enter in the input — or clicking a type="submit" button — reloads the page, because the browser's default form behavior runs before Vue's handler gets a chance to stop it by hand.
.stop is the same idea for event.stopPropagation(), and it matters most when a clickable child sits inside a clickable parent:
<template>
<li @click="openItem(index)">
{{ todo.text }}
<!-- Without .stop, deleting also fires the <li>'s own click handler,
opening the item at the exact moment it is removed -->
<button @click.stop="removeTodo(index)">✕</button>
</li>
</template>.once removes the listener after it fires once, and key modifiers (@keyup.enter, @keydown.esc) skip the if (event.key === 'Enter') check every keyboard handler used to open with. All four compose: @click.stop.prevent runs both in the order written.
What interviewers check
Whether you can explain why @click="fn()" is wrong without running it first — it is the single most common Vue template bug, and naming it correctly (a function call versus a function reference) signals you understand what a template attribute actually evaluates to, not just how to copy the syntax that happens to work.
Now practice it
2 quizzes · 1 drillReading this page is the cheap half. These are the exercises that make you use it.
- Event HandlingFix a bare @click="fn()" call, add the missing .prevent, and stop a delete click from also opening its row.DrillSolve · EasyDrillSolve · Easy
- Options API · Advanced PatternsAdvanced-tier Vue Fundamentals questions that assume event handling is already second nature.QuizTake · HardQuizTake · Hard
- Composition API · ref & reactiveEvent handlers almost always mutate a ref — this set checks that half of the picture too.QuizTake · EasyQuizTake · Easy
Read next
Template Syntax: Interpolation and Bindings
What {{ }} can and cannot render, why every dynamic attribute needs v-bind, and how :class and :style differ from a plain HTML string.
3 min
Ref vs Reactive: Vue's State Primitives
ref wraps any value behind a .value getter/setter; reactive proxies an object directly. The trade-offs explain why most Vue code reaches for ref by default.
2 min