This content originally appeared on Telerik Blogs and was authored by David Adeneye Abiodun
New to Vue 3? This guide breaks down everything you need to know about directives, from dynamic bindings to conditional rendering and custom behaviors, with examples you can use right away.
When first working with Vue, you’ll quickly notice how efficiently you can build dynamic applications, thanks in large part to one of its core features: directives. These v- prefixed attributes drive Vue’s way of syncing the DOM with your data, handling conditional rendering, binding and event listening seamlessly.
Directives define how Vue extends HTML with dynamic behaviors, forming the backbone of its reactivity. Understanding how directives observe and react to data changes is key to mastering Vue’s reactive system and creating dynamic interfaces.
In this guide, we’ll break down how Vue 3 directives work, explore the core built-in directives you’ll use every day, learn how to create them and look at some best practices to keep your templates clean and efficient. Whether you’re just starting out or brushing up on your Vue fundamentals, this post will give you a solid, practical understanding of how directives fit into Vue’s reactive world.
What Are Directives in Vue ?
At their core, directives are special HTML attributes that start with v- and apply reactive behavior to a DOM element.
Think of them as Vue’s way of giving HTML a “brain.” Instead of writing manual DOM logic, you describe what you want to happen, and Vue handles the how.
Here’s a simple example:
<p v-if="isVisible">Hello Vue!</p>
When isVisible is true, Vue renders the paragraph. When it’s false, Vue removes it from the DOM. No manual toggling, no query selectors, no manual updates. Just reactive behavior, declaratively defined.
The Anatomy of a Directive
A directive looks like this:
v-directiveName:argument.modifier="expression"
Let’s break that down:
- Directive name: E.g,
v-if,v-bind,v-model - Argument (optional): Targets something specific, like
v-bind:href - Modifier (optional): Tweaks behavior, like
.preventor.once - Expression (optional): The data or logic it depends on
Here’s a quick example showing all parts in action:
<a v-bind:href="url" @click.prevent="trackClick">Visit</a>
Here:
v-bindbinds thehrefattributes to your component’surl.@click.preventis shorthand forv-on:click.prevent, meaning “listen for a click and prevent the default browser behavior.”
Vue directives combine clean syntax with smart reactivity, and once you get used to them, you’ll rarely want to go back to manual DOM manipulation.
Built-in Directives You’ll Use Every Day
Vue ships with a handful of built-in directives that cover almost every common DOM scenario. Let’s go through the most important ones.
v-bind: Dynamic Attribute Binding
v-bind connects your templates to your component’s reactive data. You can bind any attribute—src, href, class, style and more to a reactive value.
<img v-bind:src="imageUrl" />
That’s the long form. The shorthand is much more common:
<img :src="imageUrl" />
You can even bind multiple attributes using an object:
<div v-bind="{ id: someId, class: someClass }"></div>
v-model: Two-Way Data Binding
v-model is one of Vue’s most magical features. It keeps your form inputs in sync with component data—automatically.
<input v-model="message" placeholder="Type something..." />
<p>{{ message }}</p>
Every time you type, the data updates. Every time the data changes, the input updates. No event listeners, no manual syncing. Just pure reactivity.
You can even use modifiers like .lazy, .number and .trim:
<input v-model.lazy="username" />
This tells Vue to update the data after the input loses focus, rather than on every keystroke.
v-if, v-else-if, v-else: Conditional Rendering
These control whether an element appears in the DOM based on a condition.
<p v-if="user">Welcome back, {{ user.name }}!</p>
<p v-else>Login to continue</p>
When the condition is false, the element is literally removed from the DOM, not just hidden. That’s great for performance when toggling expensive UI sections.
v-show: Toggle Visibility
v-show is similar to v-if, but instead of removing elements from the DOM, it simply toggles its display style:
<p v-show="isVisible">This element is still in the DOM</p>
Use v-show when you need frequent toggling and don’t want to destroy/recreate elements each time.
Quick rule of thumb:
- Use
v-ifwhen toggling rarely (it’s more expensive to re-create elements). - Use
v-showwhen toggling frequently (it’s faster to show/hide).
v-for: List Rendering
v-for lets you loop through arrays (or objects) to render lists.
<ul>
<li v-for="(todo, index) in todos" :key="index">
{{ todo.text }}
</li>
</ul>
Always remember to include a unique :key. It helps Vue efficiently track changes and re-render only what’s necessary.
v-on: Event Handling
This one handles DOM events. It’s used to listen for and react to user actions.
<ul>
<li v-for="(todo, index) in todos" :key="index">
{{ todo.text }}
</li>
</ul>
Or, with shorthand:
<button @click="count++">Clicked {{ count }} times</button>
You can chain event modifiers to control behavior:
<form @submit.prevent="submitForm">
<input v-model="name" />
</form>
The .prevent modifier cancels the default browser action, just like event.preventDefault() would.
v-html: Rendering Raw HTML
This directive lets you render HTML content directly into the DOM.
<div v-html="rawHtml"></div>
Use it carefully, because if rawHtml contains user-generated content, it could expose you to XSS attacks. For most cases, stick with {{ }} interpolation instead.
Modifiers and Arguments in Action
Modifiers and arguments are what make directives flexible.
<input v-model.trim="username" />
<button @click.once="handleClick">Click me once</button>
.trimcleans whitespace automatically..oncemeans the event only fires once.
These small touches make Vue’s syntax incredibly expressive and readable.
Creating Custom Directives
Sometimes, the built-ins don’t cover what you need. Maybe you need to auto-focus an input, trigger animations on scroll or integrate with a third-party library. That’s when custom directives come in.
Here’s a simple example:
app.directive('focus', {
mounted(el) {
el.focus()
}
})
Usage:
<input v-focus />
Boom—instant auto-focus whenever the element mounts.
Custom directives have their own lifecycle hooks:
createdbeforeMountmountedbeforeUpdateupdatedbeforeUnmountunmounted
Each hook gives you access to the element and the binding data, so you can respond to changes however you like.
Best Practices for Using Directives
Here are a few best practices for using directives:
- Keep logic inside directives simple. For complex logic, use computed properties or watchers.
- Avoid stacking too many directives on one element—it becomes harder to read.
- Use
v-ifandv-showappropriately based on how often the element toggles. - Create custom directives only when a pattern truly repeats across components.
Think of directives as glue between Vue’s reactivity and the DOM, not as replacements for component logic.
Common Pitfalls to Avoid
Here are some common pitfalls to avoid when using directives:
- Using
v-htmlwith untrusted content (security risk) - Forgetting to add a
keyinv-forloops - Combining
v-ifandv-foron the same element (always separate them) - Overcomplicating templates with too much logic inside directives.
Directives are powerful, but they’re meant to stay declarative. The heavy lifting should still live in your component logic.
Putting It All Together—A Mini Example
It’s one thing to read about directives, but seeing them in action ties everything together.
Below is a small Vue Todo app that shows how multiple directives work in harmony—from conditional rendering to event handling and even custom directives.
You can try this example live in CodeSandbox or StackBlitz, or drop it into your local Vue project to see it run.
You can try this example live in CodeSandbox or StackBlitz, or drop it into your local Vue project to see it run.
<template>
<div class="todo-app">
<h2>My Todo List</h2>
<!-- Input field with v-model and a custom v-focus directive -->
<input
v-model.trim="newTodo"
v-focus
placeholder="Add a new todo"
@keyup.enter="addTodo"
/>
<!-- Conditional rendering using v-if / v-else -->
<p v-if="todos.length === 0">No todos yet. Add one above </p>
<ul v-else>
<!-- Loop through todos using v-for -->
<li
v-for="(todo, index) in todos"
:key="index"
:class="{ done: todo.done }"
>
<!-- Toggling state with v-on -->
<span @click="toggleDone(todo)">
{{ todo.text }}
</span>
<!-- Delete button -->
<button @click.prevent="removeTodo(index)">x</button>
</li>
</ul>
<!-- Show summary using v-show -->
<p v-show="todos.length > 0">
{{ remaining }} remaining
</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// Custom directive: auto-focus input on mount
const vFocus = {
mounted(el) {
el.focus()
},
}
const todos = ref([])
const newTodo = ref('')
const addTodo = () => {
if (newTodo.value.trim()) {
todos.value.push({ text: newTodo.value, done: false })
newTodo.value = ''
}
}
const removeTodo = (index) => {
todos.value.splice(index, 1)
}
const toggleDone = (todo) => {
todo.done = !todo.done
}
const remaining = computed(() => todos.value.filter((t) => !t.done).length)
</script>
<style scoped>
.todo-app {
max-width: 400px;
margin: 2rem auto;
text-align: left;
font-family: system-ui, sans-serif;
}
.done {
text-decoration: line-through;
color: gray;
}
button {
margin-left: 0.5rem;
background: none;
border: none;
color: crimson;
cursor: pointer;
}
button:hover {
color: red;
}
</style>
What’s Happening Here
This small example brings together almost every directive we’ve talked about:
v-modelkeeps the input field and data in sync.v-focus(a custom directive) automatically focuses the input when mounted.v-if/v-elsehandle conditional rendering for the “todo yet” message.v-forloops through the list of todos and binds a unique:key.@click(shorthand forv-on:click) handles toggling and removing items.v-showconditionally shows the “remaining” count without removing it from the DOM.
This little component shows how directives make your templates reactive, declarative and readable. No manual DOM code, no boilerplate event logic, just Vue doing what it does best.
Wrapping It Up
Directives are one of those features that make Vue feel effortless once you understand them. Instead of micromanaging the DOM, you describe what you want to happen, and Vue handles the rest. From dynamic bindings to conditional rendering and custom behaviors, a directive is the bridge between your data and your UI.
If you’ve followed along up to this point, you’ve already touched nearly every directive you’ll use in day-to-day Vue work, and even built your first custom one. That’s a solid step toward thinking declaratively and writing cleaner, more maintainable Vue components.
In our previous post, we went a level deeper and looked at Vue’s lifecycle hooks, the what happens inside a component. Understanding the lifecycle will help you know exactly when to run logic, register listeners or clean things up as your components mount and unmount.
If you’ve been enjoying this series, stick around, we’re just getting started. Follow along for more Vue Basics guides where we’ll continue breaking down the framework piece by piece, in a way that feels practical and developer-friendly.
This content originally appeared on Telerik Blogs and was authored by David Adeneye Abiodun
David Adeneye Abiodun | Sciencx (2026-02-04T17:14:41+00:00) Vue Basics: A Comprehensive Guide to Vue 3 Directives. Retrieved from https://www.scien.cx/2026/02/04/vue-basics-a-comprehensive-guide-to-vue-3-directives/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.