Chapter 5: Vue v-if
What does v-if actually do?
v-if is Vue’s way of saying: “Only create this HTML element (and everything inside it) in the DOM if my condition is truthy. If false → don’t even render it at all.”
Key points right away:
- It’s not just hiding something (that’s v-show)
- When condition = false → the element is completely removed from the DOM (not even in devtools)
- When condition becomes true → Vue creates the element + compiles it + mounts any child components
- When it flips back to false → Vue destroys it (unmounts components, removes event listeners, etc.)
This makes v-if perfect for:
- Authentication checks (admin panel vs guest view)
- Lazy-loading heavy sections (modals, charts, maps)
- Performance-critical parts that you don’t want rendered until needed
Official Vue 3 docs say:
Conditionally render an element or a template fragment based on the truthy-ness of the expression value.
1. Basic Example – Single v-if
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<template> <div> <h2 v-if="isLoggedIn">Welcome back, Rahul! 🎉</h2> <p v-else>Please log in to continue</p> <button @click="isLoggedIn = !isLoggedIn"> Toggle Login (for demo) </button> </div> </template> <script setup> import { ref } from 'vue' const isLoggedIn = ref(false) </script> |
→ Open devtools → Elements tab When isLoggedIn = false → no <h2> exists in DOM at all When true → it appears instantly.
2. v-if + v-else-if + v-else Chain
Just like JavaScript if / else if / else
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<template> <div class="status-box"> <p v-if="score >= 90">Excellent! 🌟</p> <p v-else-if="score >= 70">Good job 👍</p> <p v-else-if="score >= 40">Keep practicing 💪</p> <p v-else>Need more work 😕</p> <input type="number" v-model.number="score" placeholder="Enter score" /> </div> </template> <script setup> import { ref } from 'vue' const score = ref(85) </script> <style scoped> .status-box { padding: 1.5rem; border: 1px solid #ddd; border-radius: 8px; } </style> |
Rules:
- v-else and v-else-ifmust immediately follow a v-if or v-else-if (no other element between)
- You can chain as many v-else-if as you want
3. The Most Important Pattern: v-if on <template>
Because v-if must be on one element, but often you want to conditionally render multiple elements or just text nodes.
Use <template v-if> — it acts as an invisible wrapper (doesn’t create extra DOM node)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<template> <div> <template v-if="showModal"> <div class="modal-overlay" @click="showModal = false"></div> <div class="modal-content"> <h3>Confirm Delete</h3> <p>Are you sure? This cannot be undone.</p> <button @click="confirmDelete">Yes, Delete</button> <button @click="showModal = false">Cancel</button> </div> </template> <button @click="showModal = true">Delete Item</button> </div> </template> <script setup> import { ref } from 'vue' const showModal = ref(false) function confirmDelete() { // delete logic... showModal.value = false } </script> <style scoped> .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); } .modal-content { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); } </style> |
→ No useless <div> wrapper in DOM when modal is closed
4. v-if vs v-show – The Big Comparison (you’ll be asked this in interviews)
| Feature | v-if | v-show |
|---|---|---|
| DOM presence | Removed completely when false | Always in DOM, just display: none |
| Initial render (false) | Very fast – nothing rendered | Slower – renders everything |
| Toggling cost | Expensive (create/destroy components) | Very cheap (just CSS toggle) |
| Best for | Rarely changing conditions, heavy content | Frequently toggled UI (tabs, accordions) |
| Lifecycle hooks | mounted/unmounted fire each time | Always mounted once |
| Use with <transition> | Great (enter/leave animations) | Also works, but element always exists |
| Memory usage (hidden) | Low (no nodes) | Higher (nodes + reactivity) |
Real 2026 rule-of-thumb:
- Use v-if for: modals, admin sections, expensive charts, anything behind auth
- Use v-show for: tabs, dropdown menus, loading spinners, frequent toggles
Advanced combo (for expensive components toggled often):
|
0 1 2 3 4 5 6 |
<HeavyChartComponent v-if="tab === 'chart'" v-show="tab === 'chart'" /> |
→ v-if prevents initial render until needed → v-show prevents destroy/re-create on tab switch
5. Common Gotchas & Pro Tips (2026 edition)
- Don’t put v-if and v-for on the same element (Vue 3 priority changed → v-if evaluated first → often breaks). Use <template v-for> + inner v-if or computed filter.
- Complex conditions? → Move to computed
|
0 1 2 3 4 5 6 |
const canSeeAdmin = computed(() => user.value.role === 'admin' && !user.value.banned) |
|
0 1 2 3 4 5 6 |
<div v-if="canSeeAdmin">Admin Tools</div> |
- v-if with async data (fetch)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<template> <div v-if="loading">Loading...</div> <div v-else-if="error">Error: {{ error.message }}</div> <div v-else> <ul> <li v-for="item in data" :key="item.id">{{ item.name }}</li> </ul> </div> </template> <script setup> import { ref, onMounted } from 'vue' const loading = ref(true) const error = ref(null) const data = ref([]) onMounted(async () => { try { const res = await fetch('/api/items') data.value = await res.json() } catch (e) { error.value = e } finally { loading.value = false } }) </script> |
- Truthy/falsy reminder: 0, ”, null, undefined, false → falsy
Summary Table – When to Choose v-if
| Scenario | Prefer v-if? | Why? |
|---|---|---|
| Modal / dialog | Yes | Heavy, infrequent |
| User role-based UI (admin vs user) | Yes | Security + perf |
| Loading / error / success states | Yes | Clean DOM |
| Tab content that switches 100×/day | Usually v-show | Cheap toggle |
| Initial page load with false condition | Yes | Faster first paint |
Practice challenge: Build a small dashboard with:
- Login form (v-if=”!loggedIn”)
- Dashboard content (v-if=”loggedIn”)
- Loading spinner (v-if=”loading”)
- Error alert (v-else-if=”error”)
Any part you want deeper?
- How v-if works with <Transition> for animations?
- v-if inside v-for pitfalls?
- Real project example with Pinia auth state?
Just tell me — we’ll keep going 🚀
Happy conditional rendering from Hyderabad! 🌶️💻
