Why are v-bind and v-on fundamental to Vue’s template syntax?

LowIntermediateVue
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

Explain why Vue templates are basically HTML + data bindings + event bindings. Cover what v-bind and v-on compile to (VNode props + event listeners), how they connect reactivity to DOM updates, and why features like v-model build on them.

Answer

Core idea

Vue templates are mostly static HTML. The two things that make templates dynamic are:
v-bind (shorthand :) — state ➜ DOM (attributes/props/class/style)
v-on (shorthand @) — DOM/component events ➜ code (which usually mutates state)

Those two directives are “fundamental” because they are the main bridges between your reactive state and the rendered output. Most higher-level conveniences (v-model, component prop/event APIs, many patterns) compile down to some combination of v-bind + v-on.

Directive

What you write in templates

What it means (conceptually)

v-bind (:)

:src="url", :class="cls", :disabled="loading"

Evaluate expression during render; when reactive deps change, Vue patches only those bound props/attrs

v-on (@)

@click="inc", @input="onInput", @submit.prevent="save"

Attach a listener; when event fires, run handler (often mutating reactive state)

Together

UI updates flow: user event ➜ state change ➜ DOM patch

This is the main “reactive loop” in Vue apps

Why templates revolve around v-bind and v-on
HTML
<script setup>
import { computed, ref } from 'vue';

const count = ref(0);
const loading = ref(false);

const buttonClass = computed(() => ({
  btn: true,
  'is-loading': loading.value,
  'is-positive': count.value > 0
}));

function inc() {
  count.value++;
}

function toggleLoading() {
  loading.value = !loading.value;
}
</script>

<template>
  <!-- v-bind: state -> DOM -->
  <button
    :class="buttonClass"
    :disabled="loading"
    :aria-busy="loading"
    @click="inc"
  >
    Count: {{ count }}
  </button>

  <!-- v-on: event -> code -> state change -->
  <button @click="toggleLoading">
    Toggle loading
  </button>
</template>
                  

What happens under the hood

Vue compiles templates into a render function that produces VNodes. v-bind becomes VNode props (including special handling for class/style and boolean props). v-on becomes event listener props like onClick.

During render, reactive reads are tracked. When reactive state changes, Vue schedules a re-render and patches only changed VNode props/listeners onto the real DOM.

JAVASCRIPT
// Pseudo-ish compiled shape (not exact Vue output)
export function render(_ctx) {
  return h('button', {
    class: _ctx.buttonClass,
    disabled: _ctx.loading,
    'aria-busy': _ctx.loading,
    onClick: _ctx.inc
  }, `Count: ${_ctx.count}`);
}

// Later, when count/loading changes:
// Vue re-runs render -> diffs VNodes -> patches only changed props on the same DOM node.
                  

Feature

How it uses v-bind/v-on

Why it matters

Props on components

<Child :value="x" />

Same binding mechanism, but targets component props instead of DOM attrs

Component events

<Child @save="onSave" />

Same listener mechanism, but listens to emitted events (child ➜ parent)

v-model

Compiles to a prop bind + an update listener

Explains why v-model is “syntax sugar”

Object spread binding

v-bind="attrs"

Pass-through attributes/props in a single place (wrapper components)

Event modifiers

@submit.prevent, @click.stop, @keyup.enter

Declarative control of default behavior and propagation

Dynamic arguments

:[name]="val", @[event]="handler"

Attribute/event names can be data-driven (use carefully; readability)

Most template features are variations of binding props and listening to events
HTML
<!-- v-model is just v-bind + v-on (Vue 3, custom component) -->
<!-- Sugar -->
<UserInput v-model="name" />

<!-- Desugared -->
<UserInput
  :modelValue="name"
  @update:modelValue="name = $event"
/>

<!-- For native inputs (conceptually) -->
<input :value="name" @input="name = $event.target.value" />
                  

Interview-ready takeaway

v-bind and v-on are the core primitives because templates become dynamic only by (1) binding reactive values into VNode props and (2) wiring events back into your code. Everything else is either additional syntax around those primitives (like v-model) or structural directives that still rely on them to express dynamic attributes and interactivity.

Similar questions
Guides
32 / 34