VueJS 3 tutorial – Attribute binding with the v-bind directive
verified on 2 September 2026 · 3 min
Double curly braces do not work inside an attribute. To bind an attribute to a piece of data, use the v-bind directive, or its colon shorthand: :src="image". The binding is reactive and runs from the data to the template.
The previous chapter displayed text inside double curly braces. Those braces only work between tags, never inside an attribute. To bind an attribute to a piece of data you need a directive: v-bind.
The product image
Our shop needs an image. Put two files in public/images/: t-shirt-bleu.svg and t-shirt-rouge.svg. Anything stored in public/ is served as is from the root of the site, so the path in the code will be /images/t-shirt-bleu.svg.
Add a ref for the image path:
<script setup>
import { ref } from 'vue'
const product = ref('T-Shirt')
const image = ref('/images/t-shirt-bleu.svg')
</script>What does not work
First instinct, writing the name of the variable in the attribute:
<img src="image">The browser then looks for a file literally named image and shows a broken image. The attribute received the string "image", not the value of the variable.
Second instinct, the curly braces:
<img src="{{ image }}">That does not work either. Vue interpolates the text of nodes, not attributes.
v-bind
The right answer is the v-bind directive. It tells Vue: “the content of this attribute is a JavaScript expression, evaluate it”.
<template>
<div class="product-display">
<div class="product-image">
<img v-bind:src="image" v-bind:alt="product">
</div>
<div class="product-info">
<h1>{{ product }}</h1>
</div>
</div>
</template>The image shows up, and the alt attribute picks up the name of the product.
The colon shorthand
v-bind is so common that it has a short form: the colon on its own.
<img :src="image" :alt="product">The two forms are strictly equivalent. The short one is what you find everywhere, the official documentation included, it is the one we will use for the rest of the tutorial.
A binding, not a copy
The binding is reactive and goes one way only: from the data to the template. Change the value and the attribute follows.
const image = ref('/images/t-shirt-bleu.svg')
// later, on click or on hover:
image.value = '/images/t-shirt-rouge.svg' // the img src changes at onceThis is the mechanism chapter 5 will use to change the image when a colour is hovered.
What else v-bind can do
Any attribute binds the same way.
<a :href="http://lienProduit">Voir la fiche</a>
<button :disabled="enRupture">Ajouter au panier</button>
<div :class="classeCourante"></div>
<div :style="{ backgroundColor: couleur }"></div>
<input :value="nom">Two cases deserve a note.
Boolean attributes such as disabled follow the HTML rule: Vue adds the attribute if the expression is true and removes it entirely if it is false. Writing :disabled="false" gives an enabled button, not a button carrying disabled="false".
The class and style attributes get special treatment: they also accept objects and arrays. That is the subject of chapter 6.
Nothing changes. v-bind and its shorthand are written the same way: it is a template directive, independent of the style the script is written in.
lienFiche value and an <a> link whose href is bound to it. Then bind the title attribute of the image to the name of the product.Common errors
:src="image".src="{{ image }}" does not work: interpolation only applies to the text between the tags.