Skip to content

v-bind and v-on

Directives are special attributes that begin with v-. They give Vue’s template engine instructions: bind this value, listen for this event, show this element conditionally. v-bind and v-on are the two you’ll use on nearly every component.

v-bind binds a DOM attribute or component prop to a JavaScript expression:

<script setup lang="ts">
const src = '/avatar.png'
const alt = 'Alice profile photo'
const isDisabled = true
</script>
<template>
<img v-bind:src="src" v-bind:alt="alt" />
<button v-bind:disabled="isDisabled">Submit</button>
</template>

The shorthand : is used everywhere in practice:

<img :src="src" :alt="alt" />
<button :disabled="isDisabled">Submit</button>

Any HTML attribute can be dynamically bound: href, class, style, type, value, title, aria-*, custom data attributes, and Vue component props.

v-on attaches an event listener:

<template>
<button v-on:click="handleClick">Click me</button>
<input v-on:input="handleInput" />
<form v-on:submit="handleSubmit">...</form>
</template>

The shorthand @ is used everywhere:

<button @click="handleClick">Click me</button>
<input @input="handleInput" />
<form @submit="handleSubmit">...</form>

For short operations, skip the function and write the expression inline:

<button @click="count++">+1</button>
<button @click="name = 'Bob'">Change name</button>
<button @click="store.deletePerson(id)">Delete</button>

Inline handlers receive the native Event object as $event if you need it:

<input @input="query = $event.target.value" />

Vue provides modifiers that chain to event listeners to handle common patterns:

<!-- prevent default (e.g., stop form from reloading the page) -->
<form @submit.prevent="handleSubmit">...</form>
<!-- stop event propagation -->
<div @click.stop="handleClick">...</div>
<!-- only fire for exact key -->
<input @keyup.enter="search" />
<input @keyup.escape="clearSearch" />
<!-- only fire once -->
<button @click.once="init">Initialize</button>

These are cleaner than calling event.preventDefault() inside every handler.

<script setup lang="ts">
function focusOn(id: string) {
store.setFocus(id)
}
</script>
<template>
<PersonCard
v-for="person in people"
:key="person.id"
:name="person.name"
@click="focusOn(person.id)"
/>
</template>
  1. Create a button that toggles a isVisible ref between true and false using an inline @click handler.
  2. Bind :class to show 'active' when isVisible is true (just use a ternary for now).
  3. Create a form with a @submit.prevent handler that logs “Form submitted!” without reloading the page.
  4. Add an input with @keyup.enter that logs the input’s current value.
  • v-bind:attr="value" / :attr="value" binds a dynamic value to an HTML attribute or component prop.
  • v-on:event="handler" / @event="handler" attaches an event listener.
  • Inline handlers work for simple expressions; function references work for anything complex.
  • Event modifiers like .prevent, .stop, .once, and .enter handle common patterns cleanly.