v-bind
v-bind (or the : shorthand) sets an attribute, prop, or class/style from an expression. The fundamental directive — used on almost every Vue template.
:attribute, :class, :style, dynamic
EXAMPLE
<script setup>
import { ref, computed } from 'vue';
const url = ref('https://example.com');
const image = ref('/photo.jpg');
const alt = ref('Profile photo');
const active = ref(true);
const color = ref('#0ea5e9');
const size = ref(48);
const meta = ref({ id: 42, role: 'admin' });
</script>
<template>
<!-- 1) Bind an attribute -->
<a v-bind:href="url">Link</a>
<a :href="url">Link</a> <!-- shorthand -->
<img :src="image" :alt="alt" />
<!-- 2) Boolean attributes -->
<button :disabled="!active">Submit</button>
<input :checked="agreed" type="checkbox" />
<!-- Vue strips the attribute entirely if value is null/undefined/false -->
<!-- 3) Bind as prop on a component -->
<UserCard :user="currentUser" :compact="true" />
<Modal :open="showModal" :title="'Confirm'" />
<!-- 4) Class binding — multiple syntaxes -->
<!-- a) Object syntax — class added when value truthy -->
<div :class="{ active: isActive, disabled: !canEdit }"></div>
<!-- b) Array syntax — combine classes -->
<div :class="['card', 'card--featured', { highlighted: isHighlighted }]"></div>
<!-- c) Computed for complex logic -->
<div :class="alertClass">…</div>
<!-- d) String -->
<div :class="`alert alert-${level}`"></div>
<!-- 5) Style binding -->
<!-- Object — kebab OR camel -->
<div :style="{ color: 'crimson', 'font-size': size + 'px', backgroundColor: color }"></div>
<!-- Array — last wins on conflict -->
<div :style="[baseStyle, error ? errorStyle : null]"></div>
<!-- CSS custom properties — themeable components -->
<div :style="{ '--accent': color }" class="themed">…</div>
<!-- 6) Bind a whole object — multiple attrs at once -->
<div v-bind="meta"></div>
<!-- Equivalent to: <div :id="meta.id" :role="meta.role"></div> -->
<!-- 7) Dynamic argument — attribute name from expression -->
<button :[eventName]="handler">Click</button>
<!-- if eventName = 'onclick', binds @click -->
<!-- 8) Modifiers -->
<button :title.prop="'tooltip'">Hover</button> <!-- as DOM prop, not attribute -->
<button :class.attr="'plain'">Plain</button> <!-- force as attribute -->
<!-- .camel is rarely needed -->
<!-- 9) Conditional rendering vs binding -->
<div :class="{ hidden: !visible }">Show me</div> <!-- still in DOM, CSS hides -->
<div v-if="visible">Show me</div> <!-- only in DOM when visible -->
<div v-show="visible">Show me</div> <!-- in DOM; display:none if hidden -->
<!-- 10) Pass slot data via :is -->
<component :is="isList ? UnorderedList : OrderedList" :items="items" />
<!-- 11) Refs are reactive — binding tracks changes -->
<input :value="name" @@input="e => name = e.target.value" />
<!-- Or use v-model for two-way -->
<!-- 12) Style bindings for animations / theming -->
<div
class="progress-bar"
:style="{ width: percent + '%', transition: 'width 300ms ease' }"
></div>
<!-- 13) Pass through reactive computed values -->
<button :class="computedClass" :disabled="computedDisabled">…</button>
</template>
<style scoped>
.themed { color: var(--accent); border: 1px solid var(--accent); }
</style>
<script setup>
// 14) Computed for complex class logic
import { computed } from 'vue';
const alertClass = computed(() => ({
'alert': true,
'alert--error': level.value === 'error',
'alert--info': level.value === 'info',
'alert--active': isActive.value,
}));
</script>
<!-- === Real patterns === -->
<!-- 15) Dynamic button styles -->
<template>
<button
:class="[
'px-4 py-2 rounded font-medium',
variantClasses,
{ 'opacity-50 cursor-not-allowed': disabled },
]"
:disabled="disabled"
:aria-busy="loading"
>
{{ label }}
</button>
</template>
<script setup>
const props = defineProps({
variant: { type: String, default: 'primary' },
disabled: Boolean,
loading: Boolean,
label: String,
});
const variantClasses = computed(() => ({
'bg-sky-500 text-white hover:bg-sky-600': props.variant === 'primary',
'bg-rose-500 text-white hover:bg-rose-600': props.variant === 'danger',
'bg-slate-200 text-slate-900 hover:bg-slate-300': props.variant === 'secondary',
})[true] || '');
</script>
<!-- 16) Form field with error -->
<input
type="email"
v-model="email"
:class="['input', { 'input--error': !!error }]"
:aria-invalid="!!error"
:aria-describedby="error ? 'email-error' : undefined"
/>
<p v-if="error" id="email-error" class="text-rose-600">{{ error }}</p>
<!-- 17) Image with fallback -->
<img
:src="avatarUrl || '/default-avatar.svg'"
:alt="`Profile photo for ${userName}`"
loading="lazy"
/>
<!-- 18) Conditional rendering shortcut -->
<button
:class="primary ? 'btn-primary' : 'btn-secondary'"
:title="helpText || undefined"
>
Save
</button>
<!-- 19) Reactive style — drag handle -->
<div
:style="{
transform: `translate(${pos.x}px, ${pos.y}px)`,
cursor: dragging ? 'grabbing' : 'grab',
}"
@@pointerdown="onDown"
@@pointermove="onMove"
@@pointerup="onUp"
>
Drag me
</div>
<!-- 20) Common bugs -->
<!-- • Forgetting : prefix → attribute treats expression as literal string -->
<!-- • Binding to refs vs raw values — use .value in script, not in template -->
<!-- • :class with array containing falsy values — silently no-op (intentional, but confusing) -->
<!-- • Inline object/style new on every render — passes new ref to memoised children -->
<!-- • Boolean attributes (disabled, checked) — v-bind handles correctly; manual concat doesn't -->
<!-- 21) Best practices -->
<!-- ✅ Use : shorthand consistently -->
<!-- ✅ Computed for complex class logic -->
<!-- ✅ Object syntax for conditional classes (clearer intent) -->
<!-- ✅ CSS custom properties via :style for theming -->
<!-- ✅ Pass undefined to hide an optional attribute -->
<!-- ✅ Don't bind objects/arrays you generate inline if children are memoised -->
Why it matters
v-bind covers attributes, props, classes, styles, and even whole objects (v-bind="meta"). Use the : shorthand everywhere and reach for computed for complex class logic — templates stay readable.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Exercise
Shorthand to bind the href attribute.
<a
"url">link</a>
A single character + href + =.
Discussion
Loading…