Explain how v-bind (:) turns reactive state into DOM attribute/prop updates: template compilation into a render function, dependency tracking during render, scheduled component updates, and how the runtime patches attributes vs DOM properties (including class/style and boolean attrs).
How does v-bind connect reactive state to DOM attributes?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Core ideav-bind (shorthand :) is not a runtime “watcher on attributes”. Vue compiles your template into a render function. During render, Vue reads the reactive values used in bindings (so they get tracked). When those reactive values change, Vue re-runs the component render and then patches only the changed attributes/DOM props on the affected elements.
<script setup>
import { ref, computed } from 'vue';
const img = ref('https://example.com/a.png');
const loading = ref(false);
const size = ref('sm');
const classes = computed(() => ({
'btn': true,
'btn--sm': size.value === 'sm',
'btn--lg': size.value === 'lg',
'is-loading': loading.value
}));
function toggle() {
loading.value = !loading.value;
size.value = size.value === 'sm' ? 'lg' : 'sm';
}
</script>
<template>
<img :src="img" :alt="loading ? 'Loading' : 'Ready'" />
<!-- boolean + aria + class object -->
<button
:class="classes"
:disabled="loading"
:aria-busy="loading"
@click="toggle"
>
Toggle
</button>
</template>
Stage | What happens | Why it matters |
|---|---|---|
1) Compile | Template | Bindings become plain JS expressions; no magic string parsing at runtime. |
2) Render in an effect | Component render runs inside a reactive effect; reading | Vue learns exactly which component depends on which reactive keys. |
3) Reactive change | A write like | Only components that used that reactive value are scheduled. |
4) Batch + flush | Multiple synchronous writes are queued/deduped; update happens in a microtask. | Avoids multiple DOM patches for back-to-back mutations. |
5) Patch | Vue diffs old vs new VNodes and applies prop/attr changes to the real DOM node. | Only changed attributes/properties are touched. |
Attribute vs DOM property (the part interviewers probe)
Vue’s patcher decides whether a binding should be applied as a DOM property (el.value, el.checked, el.innerHTML etc.) or as an HTML attribute (setAttribute). Most of the time Vue handles this for you, but knowing the difference explains edge cases (especially with form controls and boolean attributes).
Binding | Vue patch behavior (typical) | Notes / gotchas |
|---|---|---|
| Special-cased: normalized + set as | Object/array syntax is merged; avoids string concat in templates. |
| Special-cased: patches individual style properties. | Avoids rewriting the whole style string when only one value changes. |
Boolean attrs (e.g. | Sets/removes based on truthiness (and often uses the DOM property). | If false, Vue removes the attribute; DOM reflects enabled state. |
Form-related props (e.g. | Prefer DOM properties for correctness. | Attributes don’t always reflect live state; properties do. |
SVG attrs | Often must be attributes (SVG DOM differs from HTML). | Vue handles element namespace differences for you. |
What the compiler roughly generates
The exact output varies, but conceptually :src/:disabled/:class become a props object on the VNode. The runtime uses patch flags / dynamic props so it can update only what’s dynamic.
// Pseudo-ish shape (not exact Vue output)
import { openBlock, createElementBlock } from 'vue';
export function render(_ctx) {
return (
openBlock(),
createElementBlock('button', {
class: _ctx.classes,
disabled: _ctx.loading,
'aria-busy': _ctx.loading,
onClick: _ctx.toggle
}, 'Toggle', /* patchFlag */ 8 /* PROPS */, /* dynamicProps */ ['class','disabled','aria-busy'])
);
}
Useful patterns
1) Bind many attrs at once with an object (great for pass-through props / wrapper components).
2) Use dynamic argument when the attribute name itself is data-driven.
<script setup>
import { computed, ref } from 'vue';
const attrs = computed(() => ({
id: 'cta',
title: 'Call to action',
'data-track': 'signup'
}));
const attrName = ref('aria-label');
const label = ref('Sign up');
</script>
<template>
<a href="/signup" v-bind="attrs">Sign up</a>
<!-- dynamic attribute name -->
<button :[attrName]="label">Hover me</button>
</template>
Interview-ready takeawayv-bind works because the template is compiled into a render function that reads reactive state during render (dependency tracking). State changes trigger a scheduled re-render, and Vue patches only the changed VNode props—mapping them to DOM properties or HTML attributes (with special handling for class/style/booleans).