This episode covers Vue's catalog of built-in directives: v-model for two-way binding, v-show, v-cloak, and v-pre, along with their modifiers, and ends with building custom directives that use lifecycle hooks for specific DOM needs.

Directives are the heart of Vue's template "magic" — special attributes prefixed with v- that tell Vue what to do with an element. In episode 4 you already met v-bind, v-if, v-for, and v-on. Episode 7 completes that picture with the directives used most often in everyday work — and most often misunderstood.
We'll focus on v-model for two-way binding, v-show, v-cloak, and v-pre, then close with custom directives. Understanding the built-in directives keeps you from rewriting what Vue already provides, while custom directives open the door to specific DOM solutions.
v-model connects state with an input so the two stay in sync:
<script setup>
import { ref } from "vue";
const nama = ref("");
</script>
<template>
<input v-model="nama" placeholder="Nama" />
<p>Halo, {{ nama }}</p>
</template>When you type, nama updates; when nama changes in code, the input fills in. v-model="nama" is syntactic sugar for :value plus @input — two directions in a single directive.
Modifiers adjust the binding behavior:
<script setup>
import { ref } from "vue";
const angka = ref(0);
const teks = ref("");
</script>
<template>
<input v-model.number="angka" type="number" />
<input v-model.trim="teks" />
<input v-model.lazy="teks" />
</template>v-model.number converts the input into a number.v-model.trim strips leading and trailing whitespace.v-model.lazy syncs on the change event instead of every keystroke.Components can also use v-model through the modelValue contract and the update:modelValue event:
<script setup>
const props = defineProps({ modelValue: String });
const emit = defineEmits(["update:modelValue"]);
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>The parent uses it like a regular input: v-model="nama". This way any form component can support two-way binding.
v-show controls visibility through the CSS display property without removing the element from the DOM:
<script setup>
import { ref } from "vue";
const tampil = ref(true);
</script>
<template>
<div v-show="tampil">Konten yang bisa disembunyikan</div>
<button @click="tampil = !tampil">Toggle</button>
</template>v-show="tampil" only changes the display style. Unlike v-if, the element stays rendered so its internal state isn't lost — good for frequent toggling.
v-cloak hides templates that haven't been compiled yet so raw text doesn't flash on screen; v-pre skips compilation to display {{ }} syntax as-is.
When a DOM need isn't covered by the built-in directives, you can create your own. Example: auto-focusing an element when it's mounted:
const vFocus = {
mounted(el) {
el.focus();
},
};Register it locally in a component or globally through app.directive:
<script setup>
import { vFocus } from "../directives/focus";
</script>
<template>
<input v-focus placeholder="Fokus otomatis" />
</template>v-focus on the input triggers mounted(el) which calls el.focus() — the element gets focus as soon as it's rendered. With <script setup>, a directive imported as vFokus is automatically available as v-fokus.
Custom directives support lifecycle hooks just like components:
const vDemo = {
mounted(el, binding) {
console.log("dimount", binding.value);
},
updated(el, binding) {
console.log("diperbarui", binding.value);
},
unmounted(el) {
console.log("dilepas");
},
};binding.value carries the value passed when the directive is used, like v-demo="nilai". The updated hook runs whenever the element is updated, and unmounted runs when the element is removed from the DOM.
Custom directives are often used for DOM integration, such as highlighting an element based on a value:
const vHighlight = {
mounted(el, binding) {
if (binding.value) {
el.style.backgroundColor = binding.value;
}
},
};v-highlight="'yellow'" sets the background color when the element is rendered. This pattern is useful for highlighting search keywords, field errors, or data status.
Episode 7 completed your directive catalog: v-model for two-way binding with modifiers like .number, .trim, and .lazy, v-show for CSS toggling, v-cloak and v-pre for special cases, and custom directives with lifecycle hooks for specific DOM needs.
Key takeaways:
v-model is syntactic sugar for two-way binding..number, .trim, and .lazy modifiers adjust binding behavior.v-show changes display; the element stays in the DOM.v-cloak prevents template flicker; v-pre skips compilation.vNama in <script setup>.In the next episode 8, we'll move into data fetching and asynchronous UI — fetching data with fetch and Axios, managing loading state and error handling, fetching patterns based on the Composition API, and integrating Vue Query (TanStack Query) for smart caching.