Chapter 44: what is Vue Built-in Attributes
Vue Built-in Attributes (also called special template attributes, directives with shorthand, or built-in template syntax)
These are not normal HTML attributes — they are special instructions that Vue understands and compiles into reactivity, events, bindings, conditionals, loops, etc.
When you write :class, @click, v-if, v-for, v-model, etc., you’re using Vue’s built-in attributes (directives).
In Vue 3 (2026 standard), almost every interesting thing that happens in a template comes from these built-in attributes.
Let me teach you them in the order most developers actually learn/use them in real projects — with clear explanations, examples, and why each one matters.
1. The Big Four – You Will Use These 90% of the Time
| Attribute / Shorthand | Full form | Purpose | Most common real-world use-case | Example in 2026 style |
|---|---|---|---|---|
| :attr | v-bind:attr | Bind dynamic values to HTML attributes / props | class, style, src, id, data-*, disabled, :to in router-link | :class=”{ active: isActive }” |
| @event | v-on:event | Listen to DOM / custom events | clicks, submits, keyup, custom emits | @click=”increment” |
| v-model | (no shorthand needed) | Two-way data binding on form inputs | inputs, textarea, checkbox, select, custom inputs | v-model=”form.email” |
| v-if / v-else / v-show | — | Conditional rendering / visibility | show/hide modals, loading states, auth gates | v-if=”user.isLoggedIn” |
2. The Loop King – v-for
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<ul> <li v-for="todo in todos" :key="todo.id" :class="{ done: todo.done }" > {{ todo.text }} <button @click="remove(todo.id)">×</button> </li> </ul> |
Important rules you must never break (2026)
- Always add :key with a unique & stable value (usually database ID or UUID)
- Never use index as :key when items can be added/removed/reordered — causes animation bugs & lost focus
- Prefer <template v-for> when you don’t want an extra wrapper element
|
0 1 2 3 4 5 6 7 8 9 |
<template v-for="item in items" :key="item.id"> <div>Header</div> <p>{{ item.text }}</p> </template> |
3. Dynamic Binding Power – :class and :style
These two get special superpowers — Vue knows how to merge them intelligently.
Class binding – object syntax (most readable)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<div class="base-card" :class="{ 'is-active': isActive, 'is-error': hasError, 'is-loading': loading, 'premium': user.isPremium }" > Content </div> |
Class binding – array syntax (when mixing conditions)
|
0 1 2 3 4 5 6 7 8 9 10 |
<div :class="[ 'text-lg', isLarge ? 'font-bold' : 'font-normal', { 'bg-red-100': hasError, 'bg-green-100': success } ]"> |
Style binding
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div :style="{ color: textColor, fontSize: `${fontSize}px`, '--my-shadow': shadowValue }" > Styled text </div> |
4. Event Handling – @ (v-on)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<button @click="increment" @click.right="showContextMenu" @keydown.enter="submit" @submit.prevent="handleForm" @click.once="trackFirstClick" > Click me </button> |
Most common modifiers (you will use these every day)
| Modifier | What it does | Typical usage |
|---|---|---|
| .prevent | event.preventDefault() | forms, links |
| .stop | event.stopPropagation() | nested clickable elements |
| .once | listener removed after first trigger | analytics, onboarding tips |
| .self | only if event.target === element | avoid child clicks |
| .passive | improves scroll/touch performance | large scrollable areas |
| .enter | only on Enter key | form submit on input |
5. Conditional Rendering – v-if / v-else / v-show
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<div v-if="user.isAdmin"> Admin Dashboard </div> <div v-else-if="user.isEditor"> Editor Panel </div> <div v-else> Guest View </div> <!-- v-show = display: none (element stays in DOM) --> <div v-show="isLoading" class="spinner">Loading…</div> |
v-if vs v-show – quick decision table
| Use when… | Choose | Why |
|---|---|---|
| Condition rarely changes (auth, role) | v-if | removes from DOM → better perf, no hidden listeners |
| Condition toggles very often (tabs, accordions) | v-show | cheap toggle (just CSS) |
| Heavy component (chart, map) | v-if + <KeepAlive> | don’t keep it alive when hidden |
| Need enter/leave animations | both work | <Transition> wraps either |
6. Two-Way Binding Magic – v-model
|
0 1 2 3 4 5 6 7 8 |
<input v-model.trim="form.name" placeholder="Full name" /> <input v-model.number="form.age" type="number" /> <input v-model.lazy="form.comment" placeholder="Comment (updates on blur)" /> |
v-model on custom component (very common in 2026)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!-- Parent --> <CustomInput v-model="form.email" /> <!-- CustomInput.vue --> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> <script setup> defineProps(['modelValue']) defineEmits(['update:modelValue']) </script> |
Or even cleaner with defineModel() (Vue 3.3+)
|
0 1 2 3 4 5 6 7 8 |
<script setup> const modelValue = defineModel<string>() </script> |
7. Quick Reference Table – All Major Built-in Attributes (2026)
| Attribute / Shorthand | Full form | Purpose | Common gotcha / tip |
|---|---|---|---|
| :attr | v-bind:attr | Dynamic attributes / props | Always use : for JS values |
| @event | v-on:event | Event listeners | Modifiers: .prevent, .stop, .once |
| v-model | — | Two-way binding | .trim, .number, .lazy |
| v-if / v-else | — | Conditional render (remove/add) | Use with <template> for multiple elements |
| v-show | — | Conditional visibility (display: none) | Cheaper than v-if for frequent toggles |
| v-for | — | Loop over arrays/objects | Always :key – unique & stable |
| v-once | — | Render once, never update again | Good for static prices / timestamps |
| v-html | — | Render raw HTML (dangerous!) | Only trusted content – XSS risk |
| v-text | — | Set textContent (safer than {{ }}) | When you want to avoid mustache parsing |
Your Mini Practice Checklist
- Build a small card component with:
- Dynamic :class (active/error state)
- @click with .prevent and .stop
- v-if / v-show toggle
- v-for list inside with :key
- v-model on an input
- Add a custom <MyButton> component with v-model support
- Try to break one rule (no :key in v-for) → see the bug in console / animation
Any attribute still confusing? Want full examples for:
- v-for + <TransitionGroup> animation?
- Custom component with multiple v-model (v-model:foo)?
- v-html vs v-text vs {{ }} security comparison?
- All directives in one realistic dashboard card?
Just tell me — we’ll build the next clean, real-world template together 🚀
