Vue Directives & Template Syntax
Vue's template syntax and built-in directives provide powerful ways to declaratively bind data to the DOM:
Text Interpolation & v-bind
<template>
<!-- Text interpolation -->
<p>{{ message }}</p>
<p>{{ count * 2 }}</p>
<p>{{ ok ? 'YES' : 'NO' }}</p>
<!-- v-bind for attributes -->
<img v-bind:src="imageSrc" v-bind:alt="imageAlt" />
<!-- Shorthand -->
<img :src="imageSrc" :alt="imageAlt" />
<!-- Dynamic attributes -->
<button :[attributeName]="value">Button</button>
<!-- Binding multiple attributes -->
<div v-bind="objectOfAttrs"></div>
<!-- Class binding -->
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
<div :class="[activeClass, errorClass]"></div>
<!-- Style binding -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div :style="[baseStyles, overridingStyles]"></div>
</template>Conditional Rendering
<template>
<!-- v-if: Conditional rendering (removes from DOM) -->
<div v-if="type === 'A'">
Type A
</div>
<div v-else-if="type === 'B'">
Type B
</div>
<div v-else>
Type C
</div>
<!-- v-show: Toggle visibility (CSS display) -->
<div v-show="isVisible">
Toggleable content
</div>
<!-- Template for grouping without extra elements -->
<template v-if="showSection">
<h1>Title</h1>
<p>Content</p>
</template>
<!-- Use v-if for infrequent toggles, v-show for frequent -->
<Modal v-if="showModal" /> <!-- Heavy component, unmount when hidden -->
<Tooltip v-show="showTooltip" /> <!-- Light component, keep in DOM -->
</template>List Rendering
<template>
<!-- Basic v-for -->
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
<!-- v-for with index -->
<li v-for="(item, index) in items" :key="item.id">
{{ index }}. {{ item.text }}
</li>
<!-- v-for with object -->
<div v-for="(value, key, index) in user" :key="key">
{{ index }}. {{ key }}: {{ value }}
</div>
<!-- v-for with range -->
<span v-for="n in 10" :key="n">{{ n }}</span>
<!-- v-for with v-if (avoid this pattern) -->
<!-- ❌ Bad: v-if with v-for on same element -->
<li v-for="todo in todos" v-if="!todo.done" :key="todo.id">
{{ todo.text }}
</li>
<!-- ✅ Good: Use computed to filter first -->
<li v-for="todo in activeTodos" :key="todo.id">
{{ todo.text }}
</li>
<!-- ✅ Alternative: Use template -->
<template v-for="todo in todos" :key="todo.id">
<li v-if="!todo.done">
{{ todo.text }}
</li>
</template>
</template>Event Handling
<template>
<!-- Basic event handler -->
<button @click="handleClick">Click Me</button>
<!-- Inline handler -->
<button @click="count++">{{ count }}</button>
<!-- Method with parameters -->
<button @click="greet('Hello')">Greet</button>
<!-- Access event object -->
<button @click="handleClick($event)">Click</button>
<!-- Event modifiers -->
<button @click.stop="doThis">Stop Propagation</button>
<button @click.prevent="doThis">Prevent Default</button>
<form @submit.prevent="onSubmit">Submit</form>
<button @click.once="doThis">Only Once</button>
<div @click.self="doThis">Only if target is self</div>
<!-- Key modifiers -->
<input @keyup.enter="submit" />
<input @keyup.tab="handleTab" />
<input @keyup.delete="handleDelete" />
<input @keyup.esc="handleEscape" />
<input @keyup.ctrl.enter="ctrlEnterHandler" />
<!-- Mouse modifiers -->
<button @click.left="handleLeftClick">Left Click</button>
<button @click.right="handleRightClick">Right Click</button>
<button @click.middle="handleMiddleClick">Middle Click</button>
<!-- Modifier chaining -->
<button @click.stop.prevent="doThis">Chained Modifiers</button>
</template>Form Input Bindings
<script setup>
import { ref } from 'vue'
const text = ref('')
const checked = ref(false)
const checkedNames = ref([])
const picked = ref('')
const selected = ref('')
const multiSelect = ref([])
</script>
<template>
<!-- Text input -->
<input v-model="text" placeholder="Enter text" />
<p>Text: {{ text }}</p>
<!-- Checkbox -->
<input type="checkbox" v-model="checked" />
<p>Checked: {{ checked }}</p>
<!-- Multiple checkboxes -->
<input type="checkbox" value="Vue" v-model="checkedNames" />
<input type="checkbox" value="React" v-model="checkedNames" />
<input type="checkbox" value="Angular" v-model="checkedNames" />
<p>Checked: {{ checkedNames }}</p>
<!-- Radio -->
<input type="radio" value="One" v-model="picked" />
<input type="radio" value="Two" v-model="picked" />
<p>Picked: {{ picked }}</p>
<!-- Select -->
<select v-model="selected">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<!-- Modifiers -->
<input v-model.lazy="text" /> <!-- Update on change, not input -->
<input v-model.number="age" type="number" /> <!-- Convert to number -->
<input v-model.trim="message" /> <!-- Trim whitespace -->
</template>Custom Directives
Custom directives allow you to directly manipulate the DOM when needed.
// directives/focus.js
export const vFocus = {
mounted(el) {
el.focus()
},
}
// directives/click-outside.js
export const vClickOutside = {
mounted(el, binding) {
el.clickOutsideEvent = (event) => {
if (!(el === event.target || el.contains(event.target))) {
binding.value(event)
}
}
document.addEventListener('click', el.clickOutsideEvent)
},
unmounted(el) {
document.removeEventListener('click', el.clickOutsideEvent)
},
}
// Usage in component
<script setup>
import { vFocus } from './directives/focus'
import { vClickOutside } from './directives/click-outside'
const isOpen = ref(false)
const handleClickOutside = () => {
isOpen.value = false
}
</script>
<template>
<input v-focus placeholder="Auto-focused" />
<div v-click-outside="handleClickOutside">
<button @click="isOpen = true">Open Menu</button>
<div v-if="isOpen" class="menu">
Menu content
</div>
</div>
</template>Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free