Skip to main content
Vue.js for Beginners to Advanced
CHAPTER 08 Beginner

Directives in Vue.js

Updated: May 18, 2026
5 min read

# CHAPTER 8

Directives in Vue.js

1. Chapter Introduction

Directives are special HTML attributes prefixed with v- that tell Vue to do something to a DOM element. They are the syntax bridge between your reactive data and the DOM. Vue provides 14 built-in directives covering every common DOM manipulation pattern.

2. Learning Objectives

  • Use all major built-in Vue directives.
  • Understand the difference between v-if and v-show.
  • Build efficient lists with v-for and :key.
  • Create custom directives for reusable DOM behavior.

3. v-bind — Dynamic Attributes

vue
1234567891011121314
<script setup>
import { ref } from &#039;vue'
const href = ref(&#039;https://vuejs.org')
const disabled = ref(true)
const classes = ref({ active: true, disabled: false })
const styles = ref({ fontSize: &#039;18px', color: '#4f46e5' })
</script>

<template>
  <a :href="href">Vue.js</a>
  <button :disabled="disabled">Cannot click</button>
  <div :class="classes">Dynamic classes</div>
  <p :style="styles">Styled</p>
</template>

4. v-model — Two-Way Binding

vue
1234567891011121314151617181920212223242526272829303132333435363738394041
<script setup>
import { ref } from &#039;vue'
const text = ref(&#039;')
const checked = ref(false)
const selected = ref(&#039;')
const multi = ref([])
const number = ref(0)
</script>

<template>
  <!-- Text inputs -->
  <input v-model="text" placeholder="Type something" />
  <p>{{ text }}</p>

  <!-- Modifiers -->
  <input v-model.trim="text" />      <!-- trims whitespace -->
  <input v-model.lazy="text" />      <!-- update on &#039;change', not 'input' -->
  <input v-model.number="number" />  <!-- converts to number -->

  <!-- Checkbox -->
  <input type="checkbox" v-model="checked" />
  <span>{{ checked ? &#039;Checked' : 'Unchecked' }}</span>

  <!-- Radio -->
  <input type="radio" v-model="selected" value="a" /> Option A
  <input type="radio" v-model="selected" value="b" /> Option B
  <p>Selected: {{ selected }}</p>

  <!-- Select -->
  <select v-model="selected">
    <option value="">Choose</option>
    <option value="vue">Vue</option>
    <option value="react">React</option>
  </select>

  <!-- Multiple checkboxes bound to array -->
  <input type="checkbox" v-model="multi" value="HTML" /> HTML
  <input type="checkbox" v-model="multi" value="CSS" /> CSS
  <input type="checkbox" v-model="multi" value="JS" /> JS
  <p>{{ multi }}</p>
</template>

5. v-if, v-else-if, v-else

vue
1234567891011121314151617181920212223242526
<script setup>
import { ref } from &#039;vue'
const role = ref(&#039;admin')
const isVisible = ref(true)
const score = ref(85)
</script>

<template>
  <!-- v-if / v-else-if / v-else chain -->
  <div v-if="role === &#039;admin'">Admin Panel</div>
  <div v-else-if="role === &#039;editor'">Editor Panel</div>
  <div v-else>User Panel</div>

  <!-- v-if on <template> (no extra DOM element) -->
  <template v-if="score >= 90">
    <h2>Excellent!</h2>
    <p>Grade: A+</p>
  </template>
  <template v-else-if="score >= 80">
    <h2>Good job!</h2>
    <p>Grade: A</p>
  </template>
  <template v-else>
    <p>Keep trying!</p>
  </template>
</template>

6. v-if vs v-show

vue
12345678910111213141516
<template>
  <!-- v-if: Element is completely removed/added to DOM -->
  <!-- Use when: element is rarely shown, has heavy children -->
  <div v-if="isVisible">Expensive component (not in DOM when false)</div>

  <!-- v-show: Element stays in DOM, only CSS display toggles -->
  <!-- Use when: element toggles frequently (modal, dropdown, tab) -->
  <div v-show="isVisible">Toggles quickly (stays in DOM)</div>
</template>

<!-- Performance Guide:
  v-if:   Higher toggle cost (DOM manipulation), zero initial cost when false
  v-show: Lower toggle cost (CSS only), always has initial render cost
  
  Choose v-show for: dropdowns, tooltips, frequently toggled UI
  Choose v-if for:   user roles, error states, rarely shown sections -->

7. v-for — List Rendering

vue
1234567891011121314151617181920212223242526272829303132333435363738394041
<script setup>
import { ref } from &#039;vue'
const items = ref([&#039;Apple', 'Banana', 'Cherry'])
const products = ref([
  { id: 1, name: &#039;MacBook', price: 1999 },
  { id: 2, name: &#039;iPhone', price: 999 },
  { id: 3, name: &#039;iPad', price: 699 }
])
const info = ref({ name: &#039;Alice', role: 'Admin', city: 'NYC' })
</script>

<template>
  <!-- Array of primitives -->
  <ul>
    <li v-for="(item, index) in items" :key="item">
      {{ index + 1 }}. {{ item }}
    </li>
  </ul>

  <!-- Array of objects — ALWAYS use unique :key -->
  <div v-for="product in products" :key="product.id" class="product">
    <h4>{{ product.name }}</h4>
    <span>${{ product.price }}</span>
  </div>

  <!-- Object properties -->
  <dl>
    <template v-for="(value, key) in info" :key="key">
      <dt>{{ key }}</dt>
      <dd>{{ value }}</dd>
    </template>
  </dl>

  <!-- Range -->
  <span v-for="n in 5" :key="n">{{ n }} </span>  <!-- 1 2 3 4 5 -->

  <!-- Empty state -->
  <template v-if="products.length === 0">
    <p>No products found.</p>
  </template>
</template>

8. Custom Directives

vue
1234567891011121314151617181920212223242526272829303132333435363738
<script setup>
// Define a custom directive in <script setup>
const vFocus = {
  mounted(el) { el.focus() }  // Auto-focus when element mounts
}

const vColor = {
  mounted(el, binding) {
    el.style.color = binding.value
  },
  updated(el, binding) {
    el.style.color = binding.value
  }
}

const vClickOutside = {
  mounted(el, binding) {
    el._clickOutside = (event) => {
      if (!el.contains(event.target)) {
        binding.value(event)
      }
    }
    document.addEventListener(&#039;click', el._clickOutside)
  },
  unmounted(el) {
    document.removeEventListener(&#039;click', el._clickOutside)
  }
}
</script>

<template>
  <!-- Custom directive: v-directiveName -->
  <input v-focus placeholder="Auto-focused!" />
  <p v-color="&#039;#6366f1'">Purple text via directive</p>
  <div v-click-outside="() => console.log(&#039;Clicked outside!')">
    Click outside me
  </div>
</template>

9. Other Useful Directives

vue
1234567891011121314
<template>
  <!-- v-once: Render once, no reactive updates -->
  <h1 v-once>{{ title }}</h1>

  <!-- v-pre: Skip Vue compilation — renders raw {{ mustaches }} -->
  <pre v-pre>{{ this will not be compiled }}</pre>

  <!-- v-cloak: Hides uncompiled template flicker -->
  <!-- (CSS required: [v-cloak] { display: none }) -->
  <div v-cloak>{{ message }}</div>

  <!-- v-memo: Memoize a sub-tree (Vue 3.2+) — skip re-render if deps unchanged -->
  <div v-memo="[item.id, item.status]">{{ item.name }}</div>
</template>

10. Common Mistakes

  • v-for without :key: Vue warns and cannot efficiently update the list. Always provide a unique, stable key (ideally an ID from your data).
  • Using v-if and v-for on the same element: Never do <li v-if="..." v-for="...">. v-for has higher priority. Use <template v-for> outside with <li v-if> inside.

11. MCQs

Question 1

v-model.number does?

Question 2

What is the key difference between v-if and v-show?

Question 3

:key in v-for should be?

Question 4

Custom directive is accessed with?

Question 5

v-model.lazy updates on?

Question 6

v-for with range v-for="n in 5" produces?

Question 7

What does v-pre do?

Question 8

Best approach for v-if + v-for together?

Question 9

Custom directive mounted hook receives?

Question 10

v-model.trim does?

12. Interview Questions

  • Q: What is the difference between v-if and v-show? When do you choose each?
  • Q: Why should you never use v-if and v-for on the same element?

13. Summary

Vue's directive system provides a declarative interface for every common DOM behavior. v-bind and v-model handle data flow. v-if/v-show handle conditional display. v-for handles list rendering. Custom directives encapsulate DOM-specific logic into reusable plugins.

14. Next Chapter Recommendation

In Chapter 9: Event Handling in Vue.js, we dive deep into event modifiers, keyboard shortcuts, mouse events, and build an interactive todo application.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·