Chapter 71: Vue v-bind Directive
V-bind (almost always written with its shorthand 🙂 is the directive that lets you dynamically bind values to HTML attributes, props, or even special Vue features.
Without v-bind, everything in your template would be static strings. With v-bind, your template becomes alive — it can react to data changes, computed values, props, and JavaScript expressions.
1. The Two Faces of v-bind
There are two completely different worlds people live in when they talk about v-bind:
World A – Beginners & people who learned Vue 2 first They think v-bind = “how to set src, href, class, style, disabled, etc. dynamically”
World B – Intermediate & advanced developers in 2026 They think v-bind = the way to pass props to child components + dynamic attributes + spread $attrs + CSS variable binding with v-bind() in <style>
Both worlds are correct — but World B is where the real power lives in modern Vue 3.
Let’s cover both worlds step by step.
World A – Classic v-bind (Dynamic Attributes)
This is what everyone learns first.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<template> <div> <!-- Static attribute – boring --> <img src="https://example.com/logo.png" alt="Logo"> <!-- Dynamic with v-bind / : --> <img :src="logoUrl" :alt="altText" :class="{ 'rounded-full': isCircle }" :style="{ width: imageSize + 'px' }" :title="tooltip" :disabled="isDisabled" > </div> </template> <script setup> import { ref, computed } from 'vue' const logoUrl = ref('https://vuejs.org/images/logo.png') const altText = ref('Vue Logo') const isCircle = ref(true) const imageSize = ref(180) const tooltip = ref('Official Vue logo') const isDisabled = ref(false) </script> |
Shorthand rule (memorize this forever)
|
0 1 2 3 4 5 6 7 8 |
<!-- These are 100% identical --> <img v-bind:src="logoUrl"> <img :src="logoUrl"> |
Most common dynamic bindings
| Attribute | Shorthand syntax | Typical real-world usage |
|---|---|---|
| class | :class | Conditional classes (active, error, dark mode) |
| style | :style | Dynamic width, color, transform |
| src / href | :src / :href | Images from API, dynamic links |
| id | :id | Unique IDs, anchor targets |
| disabled | :disabled | Form buttons, inputs during loading |
| data-* | :data-testid | Testing attributes |
| aria-* | :aria-label | Accessibility |
| Custom prop | :user=”currentUser” | Passing data to child components |
World B – Modern v-bind Power (2026 Style – This is where the magic happens)
1. :class & :style superpowers (object & array syntax)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div :class="[ 'text-lg', isActive ? 'bg-blue-600 text-white' : 'bg-gray-200', { 'border-red-500': hasError, 'p-4': isLarge } ]" :style="{ fontSize: fontSize + 'px', '--primary-color': themeColor, opacity: isVisible ? 1 : 0.5 }" > Content </div> |
→ Vue intelligently merges classes and styles from parent + child + dynamic bindings
2. Spreading $attrs (the #1 use-case in UI libraries)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!-- FancyButton.vue – perfect forwarding --> <template> <!-- inheritAttrs: false + v-bind="$attrs" = magic --> <button v-bind="$attrs" class="fancy-btn" :class="{ 'btn-loading': loading }" > <slot /> </button> </template> <script setup lang="ts"> defineProps<{ loading?: boolean }>() defineOptions({ inheritAttrs: false }) // ← disables auto inheritance </script> |
Parent can write normal HTML attributes:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<FancyButton type="submit" class="w-full" disabled data-testid="submit-btn" @click="submitForm" > Submit </FancyButton> |
→ All attributes + listeners land perfectly on the real <button>
3. v-bind() in <style scoped> – Reactive CSS Variables (very modern)
Since Vue 3.2+, you can use reactive values directly in CSS:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<script setup> const primary = ref('#3b82f6') const fontSize = ref(16) </script> <style scoped> button { background: v-bind(primary); font-size: v-bind(fontSize + 'px'); box-shadow: 0 4px 15px v-bind('`0 0 25px ' + primary + '50`'); } </style> |
→ Change primary.value → CSS updates instantly — no inline styles cluttering HTML
Quick Summary Table – v-bind / : in 2026
| Use-case | Syntax Example | Tip / Gotcha |
|---|---|---|
| Dynamic HTML attribute | :src=”imageUrl” | Always use : for JS values |
| Conditional class (object) | :class=”{ active: isActive, error: hasError }” | Cleanest & most readable |
| Multiple classes (array) | :class=”[‘base’, isLarge ? ‘text-xl’ : ‘text-base’]” | Mix static + dynamic |
| Inline style object | :style=”{ color: textColor, fontSize: size + ‘px’ }” | Good for dynamic values |
| Spread all attrs to child/native elem | v-bind=”$attrs” | UI library must-have |
| Reactive CSS var in <style scoped> | color: v-bind(myColor) | Vue 3.2+ magic |
| Pass object as prop | :user=”currentUser” | Object is reactive if ref/reactive |
Pro Tips from Real Projects (Hyderabad 2026)
- Always use the shorthand : — :class is cleaner than v-bind:class
- For class and style — Vue merges intelligently (does not overwrite)
- Use v-bind=”$attrs” + inheritAttrs: false in every UI library component
- Prefer object syntax for :class — easiest to read & extend
- Use array syntax when mixing static + conditional classes
- In <style scoped> v-bind() → you can even do expressions: v-bind(‘isDark ? “#111” : “#fff”‘)
Your mini practice task:
- Create a StatusCard component
- Bind dynamic :class (success/error/info) based on prop
- Bind :style for background & border color
- Add v-bind=”$attrs” so parent can pass data-testid, id, @click, etc.
Any part confusing? Want full examples for:
- :class object vs array vs mixed syntax comparison?
- Real UI button/input with full $attrs forwarding?
- Reactive CSS variables in <style scoped> + dark mode toggle?
- Dynamic component prop passing with :is + v-bind?
Just tell me — we’ll build the next clean, dynamic component together 🚀
