Vue's "template syntax" is just a fancy name for the special bits of code you sprinkle into your HTML to make it dynamic. This guide walks through every major piece, one at a time, with examples you can copy and run.
All examples use this same starter shell — just swap out what's inside <div id="app"> and inside data():
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- your template goes here -->
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
// your data goes here
}
}
}).mount('#app')
</script>
</body>
</html>
1. Text interpolation: {{ }}
This is the most basic thing you'll do in Vue: showing a piece of data as text.
<div id="app">
<p>{{ message }}</p>
</div>
<script>
createApp({
data() {
return {
message: 'Hello there!'
}
}
}).mount('#app')
</script>
Whatever is inside {{ }} gets evaluated and swapped in as text. You can even do small calculations in there:
<p>{{ 1 + 1 }}</p> <!-- shows: 2 -->
<p>{{ message.toUpperCase() }}</p> <!-- shows: HELLO THERE! -->
Think of {{ }} as a window into your data — whatever changes on the inside instantly shows on the outside.
One rule: {{ }} only works for text. If you try to use it to set an attribute like <p {{ someClass }}>, it won't work — for that, you need directives, which is next.
2. Directives: the v- prefixed attributes
A directive is a special HTML attribute that starts with v-. It tells Vue "do something special with this element." Let's go through the important ones.
v-bind — binding attributes to data
Say you want an image's src, or a link's href, to come from your data instead of being hardcoded.
<div id="app">
<img v-bind:src="imageUrl">
<a v-bind:href="linkUrl">Visit site</a>
</div>
<script>
createApp({
data() {
return {
imageUrl: 'https://vuejs.org/images/logo.png',
linkUrl: 'https://vuejs.org'
}
}
}).mount('#app')
</script>
v-bind:src="imageUrl" means "set the src attribute to whatever imageUrl equals." If imageUrl changes later, the image updates automatically.
Shortcut: v-bind: is used so often that Vue lets you shorten it to just a colon:
<img :src="imageUrl">
These two lines do exactly the same thing. Most real Vue code uses the short version.
v-on — listening for events
This is how you react to things like clicks, typing, or form submissions.
<div id="app">
<button v-on:click="sayHi">Click me</button>
</div>
<script>
createApp({
methods: {
sayHi() {
alert('Hi there!')
}
}
}).mount('#app')
</script>
Shortcut: v-on: can be shortened to @.
<button @click="sayHi">Click me</button>
You can also run small bits of code directly, without a full method:
<button @click="count++">Add one</button>
<button @click="count = 0">Reset</button>
v-model — two-way binding on form inputs
This connects an input field directly to a piece of data, in both directions: typing in the box updates the data, and changing the data updates the box.
<div id="app">
<input v-model="name" placeholder="Type your name">
<p>Hello, {{ name }}!</p>
</div>
<script>
createApp({
data() {
return { name: '' }
}
}).mount('#app')
</script>
Type in the box and watch the greeting update instantly, live, with zero extra code.
v-if, v-else-if, v-else — conditional display
Shows or hides elements based on whether something is true or false.
<div id="app">
<p v-if="score >= 90">Grade: A 🎉</p>
<p v-else-if="score >= 70">Grade: B</p>
<p v-else>Grade: keep studying</p>
</div>
<script>
createApp({
data() {
return { score: 82 }
}
}).mount('#app')
</script>
Vue checks each condition top to bottom, just like a normal if / else if / else in any programming language, and only shows the first one that matches.
There's also v-show, which looks similar but behaves differently:
<p v-show="isVisible">I'm shown or hidden with CSS, not removed from the page</p>
v-ifcompletely adds/removes the element from the page.v-showkeeps the element on the page but toggles CSS to hide it.
Rule of thumb: use v-show for things that toggle often (like a dropdown), and v-if for things that rarely change (like showing a login form vs. a dashboard).
v-for — looping over lists
<div id="app">
<ul>
<li v-for="animal in animals">{{ animal }}</li>
</ul>
</div>
<script>
createApp({
data() {
return {
animals: ['Dog', 'Cat', 'Fox']
}
}
}).mount('#app')
</script>
If you also want the position of each item, grab the index like this:
<li v-for="(animal, index) in animals">
{{ index }}: {{ animal }}
</li>
Important: when using v-for, always add a :key with something unique, so Vue can track each item correctly if the list changes:
<li v-for="animal in animals" :key="animal">{{ animal }}</li>
Think of :key as a name tag — it helps Vue tell items apart even after the list is reordered or edited.
3. Dynamic classes and styles
A very common need: change an element's CSS class or style based on data. Vue has clean shortcuts for this.
Dynamic classes
<div id="app">
<p :class="{ active: isActive }">This paragraph might be highlighted</p>
<button @click="isActive = !isActive">Toggle highlight</button>
</div>
<script>
createApp({
data() {
return { isActive: false }
}
}).mount('#app')
</script>
<style>
.active { background: yellow; }
</style>
:class="{ active: isActive }" reads as: "apply the CSS class active only when isActive is true." Click the button and watch the highlight toggle on and off.
Dynamic inline styles
<div id="app">
<p :style="{ color: textColor, fontSize: fontSize + 'px' }">
Colorful text!
</p>
</div>
<script>
createApp({
data() {
return {
textColor: 'purple',
fontSize: 24
}
}
}).mount('#app')
</script>
This directly sets style="color: purple; font-size: 24px;" on the element, but keeps the values connected to your data so they can change dynamically.
4. Attribute binding shortcuts recap
| Long form | Shortcut | Meaning |
|---|---|---|
v-bind:src="x" |
:src="x" |
Bind an attribute to data |
v-on:click="x" |
@click="x" |
Listen for an event |
Almost all real-world Vue code uses the shortcuts (: and @), so it's worth getting comfortable reading them even though the long form is what's "actually" happening under the hood.
5. Putting it all together
Here's a small example combining several pieces of template syntax into one working mini-app: a simple product card.
<div id="app">
<div :class="{ card: true, 'out-of-stock': !inStock }">
<h2>{{ productName }}</h2>
<p>Price: ${{ price }}</p>
<p v-if="inStock">✅ In stock</p>
<p v-else>❌ Out of stock</p>
<button @click="inStock = !inStock">Toggle stock status</button>
<ul>
<li v-for="feature in features" :key="feature">{{ feature }}</li>
</ul>
</div>
</div>
<script>
createApp({
data() {
return {
productName: 'Wireless Headphones',
price: 49.99,
inStock: true,
features: ['Bluetooth 5.0', '20-hour battery', 'Noise cancelling']
}
}
}).mount('#app')
</script>
Every technique from this article shows up here: {{ }} for text, :class for dynamic styling, v-if/v-else for conditional content, @click for interaction, and v-for for the feature list.
6. Cheat sheet
| Syntax | Purpose |
|---|---|
{{ value }} |
Insert text into the page |
:attribute="value" |
Bind an attribute (src, href, class, style, etc.) to data |
@event="handler" |
Run code when an event happens (click, input, submit...) |
v-model="value" |
Two-way link between a form input and data |
v-if / v-else-if / v-else |
Show/hide by adding or removing from the page |
v-show |
Show/hide using CSS, element stays in the page |
v-for="item in list" |
Repeat an element for each item in a list |
:key="uniqueValue" |
Helps Vue track list items (always pair with v-for) |
The pattern behind all of it is the same: your data describes the truth, and the template describes how that truth should appear on the page. Change the data, and Vue rewrites the page for you.