Skip to content

VueJS - Template

Vue uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying component instance’s data. All Vue templates are syntactically valid HTML that can be parsed by spec-compliant browsers and HTML parsers.

Under the hood, Vue compiles templates into highly optimized JavaScript code, often in the form of Virtual DOM render functions, which efficiently update the actual DOM.

The most basic form of data binding is text interpolation using the “Mustache” syntax (double curly braces):

<p>Message: {{ message }}</p>

The content inside the mustaches will be replaced with the value of the message property from the component’s state. It will also be automatically updated whenever the message property changes.

You can only use single expressions inside interpolations, not statements. For example, {{ count + 1 }} is valid, but {{ var a = 1 }} or {{ if (ok) { return message } }} are not.

The double mustaches interpret the data as plain text, not HTML. If you need to output actual HTML content stored in your data, you must use the v-html directive.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue 3 Template Syntax</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app">
<h2>Text Interpolation:</h2>
<p>{{ rawHtmlContent }}</p> <!-- This will show HTML tags as text -->
<h2>Using v-html:</h2>
<div v-html="rawHtmlContent"></div> <!-- This will render the HTML -->
</div>
<script>
const { createApp, ref } = Vue;
createApp({
setup() {
const rawHtmlContent = ref('<span style="color: red; font-weight: bold;">This should be red and bold.</span>');
return { rawHtmlContent };
}
}).mount('#app');
</script>
</body>
</html>

In the browser:

  • The first paragraph will literally display: <span style="color: red; font-weight: bold;">This should be red and bold.</span>
  • The second div, using v-html, will display the text “This should be red and bold.” styled in red and bold, as the HTML is parsed and rendered.

Dynamically rendering arbitrary HTML on your website can be very dangerous as it can easily lead to Cross-Site Scripting (XSS) attacks. Only use v-html on trusted content and never on user-provided content. If dealing with user input that needs rich text formatting, use libraries that sanitize the HTML first.

Mustache syntax cannot be used inside HTML attributes. Instead, use the v-bind directive to bind an attribute’s value to a component’s state.

<!-- Bind element ID -->
<div v-bind:id="dynamicId">...</div>
<!-- Bind element class -->
<div v-bind:class="activeClass">...</div>
<!-- Bind boolean attribute (e.g., disabled) -->
<button v-bind:disabled="isButtonDisabled">Click Me</button>
<!-- Bind image source -->
<img v-bind:src="imageUrl">

Because v-bind is used so frequently, Vue provides a convenient shorthand – a colon (:):

<!-- Shorthand for v-bind:id -->
<div :id="dynamicId">...</div>
<!-- Shorthand for v-bind:class -->
<div :class="activeClass">...</div>
<!-- Shorthand for v-bind:disabled -->
<button :disabled="isButtonDisabled">Click Me</button>
<!-- Shorthand for v-bind:src -->
<img :src="imageUrl">

This shorthand is widely used in the Vue community.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue 3 Attribute Binding</title>
<script src="https://unpkg.com/vue@3"></script>
<style>
.active { border: 2px solid blue; }
.text-danger { color: red; }
img { max-width: 100px; display: block; margin-top: 10px; }
</style>
</head>
<body>
<div id="app">
<h1 :id="headingId">My Dynamic Heading</h1>
<p :class="statusClass">Status message</p>
<button @click="isError = !isError">Toggle Error State</button>
<button :disabled="isDisabled">{{ isDisabled ? 'Processing...' : 'Submit' }}</button>
<button @click="isDisabled = !isDisabled">Toggle Disabled</button>
<img :src="currentImage" alt="Dynamic Image">
<button @click="changeImage">Change Image</button>
<!-- Binding multiple attributes -->
<div v-bind="divAttributes">Div with multiple bound attributes</div>
</div>
<script>
const { createApp, ref, computed, reactive } = Vue;
createApp({
setup() {
const headingId = ref('main-heading');
const isError = ref(false);
const isDisabled = ref(false);
const images = [
'https://via.placeholder.com/100/0000FF/FFFFFF?text=Image1',
'https://via.placeholder.com/100/FF0000/FFFFFF?text=Image2'
];
const currentImageIndex = ref(0);
// Computed property for dynamic class
const statusClass = computed(() => {
return {
active: !isError.value,
'text-danger': isError.value
};
});
const currentImage = computed(() => images[currentImageIndex.value]);
function changeImage() {
currentImageIndex.value = (currentImageIndex.value + 1) % images.length;
}
// Object for binding multiple attributes
const divAttributes = reactive({
'data-index': 1,
title: 'This is a dynamic title'
});
return {
headingId,
isError,
statusClass,
isDisabled,
currentImage,
changeImage,
divAttributes
};
}
}).mount('#app');
</script>
</body>
</html>

In this example:

  • :id binds the <h1>’s ID.
  • :class binds classes dynamically based on the isError state using an object syntax (more on class/style binding later).
  • :disabled binds the button’s disabled state.
  • :src binds the image source dynamically.
  • Clicking the buttons updates the reactive state, and Vue automatically updates the corresponding attributes in the DOM.
  • An object divAttributes is bound using v-bind (without an argument) to set multiple attributes at once.

Vue supports the full power of JavaScript expressions inside all data bindings (mustaches and directive values).

{{ count + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="'list-' + id"></div>

However, each binding can only contain one single expression. Complex logic should typically be handled by computed properties or methods within the component’s script section.

Restrictions: Expressions are sandboxed and only have access to component properties and a limited allowlist of globals (like Math, Date). You cannot access arbitrary user-defined globals.

This chapter covered the basics of Vue’s template syntax for displaying text and binding attributes. Subsequent chapters will explore other directives like v-on for events and v-model for form binding, as well as more advanced techniques for class and style binding.

Further Reading: Vue’s official documentation on Template Syntax: https://vuejs.org/guide/essentials/template-syntax.html