Attribute Bindings with v-bind

+15 Mana ✨

Mustaches cannot be used inside HTML attributes. Instead, use the v-bind directive to dynamically bind attributes to JavaScript expressions.

Basic Attribute Binding

The v-bind directive binds an attribute to a dynamic value:

vue
<script setup>
import { ref } from 'vue'

const imageUrl = ref('/images/logo.png')
const linkUrl = ref('https://vuejs.org')
const inputId = ref('email-input')
</script>

<template>
  <!-- Bind the src attribute -->
  <img v-bind:src="imageUrl" alt="Logo" />
  
  <!-- Bind the href attribute -->
  <a v-bind:href="linkUrl">Vue.js Website</a>
  
  <!-- Bind the id attribute -->
  <input v-bind:id="inputId" type="email" />
  <label v-bind:for="inputId">Email:</label>
</template>

Shorthand Syntax: :

Because v-bind is so common, Vue provides a shorthand—just use : before the attribute:

vue
<template>
  <!-- Full syntax -->
  <img v-bind:src="imageUrl" />
  
  <!-- Shorthand (recommended) -->
  <img :src="imageUrl" />
  <a :href="linkUrl">Link</a>
  <input :id="inputId" />
</template>

The shorthand is the convention you'll see in most Vue code.

Same-name Shorthand (Vue 3.4+)

When the attribute name matches the JavaScript variable name, you can use an even shorter syntax:

vue
<script setup>
import { ref } from 'vue'

const id = ref('my-element')
const src = ref('/images/photo.jpg')
const href = ref('https://example.com')
</script>

<template>
  <!-- Same as :id="id" -->
  <div :id>Content</div>
  
  <!-- Same as :src="src" -->
  <img :src alt="Photo" />
  
  <!-- Same as :href="href" -->
  <a :href>Link</a>
</template>

Boolean Attributes

Boolean attributes indicate true/false values by their presence. v-bind handles them specially:

vue
<script setup>
import { ref } from 'vue'

const isDisabled = ref(true)
const isRequired = ref(false)
const isChecked = ref(true)
</script>

<template>
  <!-- Renders: <button disabled>...</button> -->
  <button :disabled="isDisabled">Can't click me</button>
  
  <!-- Renders: <input> (no required attribute) -->
  <input :required="isRequired" />
  
  <!-- Renders: <input type="checkbox" checked> -->
  <input type="checkbox" :checked="isChecked" />
</template>

Rules for boolean attributes:

  • Truthy value (including "" empty string): Attribute is included
  • Falsy value (false, null, undefined): Attribute is omitted

Binding Multiple Attributes

You can bind multiple attributes at once using an object:

vue
<script setup>
import { ref, reactive } from 'vue'

const inputAttrs = reactive({
  id: 'email-field',
  type: 'email',
  placeholder: 'Enter your email',
  required: true
})

const imageAttrs = ref({
  src: '/images/avatar.jpg',
  alt: 'User avatar',
  width: 100,
  height: 100
})
</script>

<template>
  <!-- Bind all attributes from the object -->
  <input v-bind="inputAttrs" />
  <!-- Renders: <input id="email-field" type="email" placeholder="Enter your email" required> -->
  
  <img v-bind="imageAttrs" />
  <!-- Renders: <img src="/images/avatar.jpg" alt="User avatar" width="100" height="100"> -->
</template>

Dynamic Attribute Names

You can also dynamically bind attribute names using bracket syntax:

vue
<script setup>
import { ref } from 'vue'

const attributeName = ref('title')
const attributeValue = ref('This is a tooltip')
</script>

<template>
  <!-- Dynamic attribute name -->
  <div :[attributeName]="attributeValue">
    Hover over me!
  </div>
  <!-- Renders: <div title="This is a tooltip">...</div> -->
</template>

Common Patterns

Conditional Classes and Styles

vue
<script setup>
import { ref } from 'vue'

const isActive = ref(true)
const hasError = ref(false)
const fontSize = ref(16)
</script>

<template>
  <!-- Binding to expressions -->
  <div :class="isActive ? 'active' : 'inactive'">Status</div>
  
  <!-- Dynamic styles -->
  <p :style="{ fontSize: fontSize + 'px' }">Styled text</p>
</template>

Image Sources

vue
<script setup>
import { ref, computed } from 'vue'

const userId = ref(42)
const avatarUrl = computed(() => `/api/users/${userId.value}/avatar`)
</script>

<template>
  <img :src="avatarUrl" :alt="`User ${userId} avatar`" />
</template>

Aria Attributes for Accessibility

vue
<script setup>
import { ref } from 'vue'

const isExpanded = ref(false)
const menuId = ref('main-menu')
</script>

<template>
  <button 
    :aria-expanded="isExpanded" 
    :aria-controls="menuId"
    @click="isExpanded = !isExpanded"
  >
    Toggle Menu
  </button>
  <nav :id="menuId" v-show="isExpanded">
    <!-- Menu items -->
  </nav>
</template>
✓ Completed