Chapter 75: Vue v-if Directive
V-if
This is the directive you reach for whenever you want to conditionally render (or completely remove) an element or block of template based on a JavaScript expression.
It’s not the same as v-show — and understanding the difference is what separates beginner code from clean, performant production code.
1. The Core Idea – What v-if Actually Does
v-if tells Vue:
“Only include this element (and everything inside it) in the real DOM if this expression is true. If it becomes false, completely destroy the element and all its children — remove it from the DOM.”
This is very different from just hiding something with CSS.
2. v-if vs v-show – The Most Important Comparison (Memorize This Table)
| Feature | v-if | v-show | When to choose which |
|---|---|---|---|
| How it works | Adds/removes element from DOM | Changes display: none / display: block | — |
| Initial render cost | Only renders when true | Always renders (even when hidden) | v-if wins if expensive |
| Re-render cost when toggling | Full mount/unmount cycle | Very cheap (just CSS toggle) | v-show wins if frequent toggle |
| Lifecycle hooks on toggle | onMounted / onUnmounted run every time | Never — component stays mounted | v-if = lifecycle reset |
| Animations | Works perfectly with <Transition> | Works, but element always exists | Both good |
| Best for | Content that rarely changes (modals, auth gates, heavy components) | Content that toggles often (tabs, accordions, tooltips, loading spinners) | — |
| DOM presence | Gone when false | Hidden but present | v-if = cleaner DOM |
Golden rule of thumb (2026)
- Use v-if when:
- The condition changes rarely (login status, user role, modal open/close)
- The content is expensive to render (charts, maps, large lists, complex components)
- You want true destruction (unmount → cleanup timers, event listeners, memory)
- Use v-show when:
- The condition changes very often (tab switching, accordion open/close, hover tooltips)
- You want instant toggle (no mount/unmount cost)
- The component should remember state (form values, scroll position, internal counters)
3. Real, Practical Example – Login Form with Conditional Content
|
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
<template> <div class="auth-container"> <!-- v-if + v-else-if + v-else chain --> <div v-if="isLoggedIn" class="welcome"> <h2>Welcome back, {{ user.name }}!</h2> <p>Last login: {{ user.lastLogin | formatDate }}</p> <button @click="logout">Logout</button> </div> <div v-else-if="isLoading" class="loading"> <div class="spinner"></div> <p>Checking authentication...</p> </div> <div v-else class="login-form"> <h2>Please Sign In</h2> <input v-model.trim="credentials.email" type="email" placeholder="Email" required /> <input v-model.trim="credentials.password" type="password" placeholder="Password" required /> <button :disabled="!formIsValid || isSubmitting" @click="login" > {{ isSubmitting ? 'Signing in…' : 'Sign In' }} </button> <p v-if="loginError" class="error">{{ loginError }}</p> </div> </div> </template> <script setup lang="ts"> import { ref, computed } from 'vue' const isLoggedIn = ref(false) const isLoading = ref(true) const isSubmitting = ref(false) const loginError = ref<string | null>(null) const user = ref({ name: 'Rahul Sharma', lastLogin: new Date().toISOString() }) const credentials = reactive({ email: '', password: '' }) const formIsValid = computed(() => { return ( credentials.email.includes('@') && credentials.password.length >= 6 ) }) // Simulate initial auth check setTimeout(() => { isLoading.value = false // In real app: check token, API call, etc. }, 1500) async function login() { isSubmitting.value = true loginError.value = null try { // Fake API delay await new Promise(r => setTimeout(r, 1200)) // Success isLoggedIn.value = true } catch (err: any) { loginError.value = err.message || 'Login failed. Please try again.' } finally { isSubmitting.value = false } } function logout() { isLoggedIn.value = false credentials.email = '' credentials.password = '' } </script> <style scoped> .auth-container { max-width: 420px; margin: 4rem auto; padding: 2rem; background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); } .welcome { text-align: center; } .loading { text-align: center; padding: 4rem; color: #6b7280; } .login-form input { width: 100%; padding: 0.9rem; margin: 0.8rem 0; border: 1px solid #d1d5db; border-radius: 6px; } button { width: 100%; padding: 0.9rem; background: #3b82f6; color: white; border: none; border-radius: 6px; margin-top: 1rem; cursor: pointer; } button:disabled { opacity: 0.6; cursor: not-allowed; } .error { color: #dc2626; margin-top: 1rem; text-align: center; } </style> |
Key Lessons from This Example
- v-if → completely removes login form when user is logged in → no unnecessary DOM, no hidden listeners
- v-else-if → loading state only shown during initial check
- v-else → default fallback when neither logged in nor loading
- No extra wrapper <div> needed — Vue handles conditional blocks cleanly
Quick Reference Table – v-if Family (2026 Must-Know)
| Directive | When to use it | DOM presence when false | Re-mounts on true? | Cost on toggle | Animation support |
|---|---|---|---|---|---|
| v-if | Condition changes rarely, heavy content | Removed | Yes | Higher | Perfect with <Transition> |
| v-else-if | Chain of conditions | Removed | Yes | Higher | Yes |
| v-else | Default fallback | Removed | Yes | Higher | Yes |
| v-show | Condition changes frequently, light content | Hidden (display:none) | No | Very low | Works, but element always exists |
Pro Tips from Real Projects (Hyderabad 2026)
-
Use v-if for:
- Modals / dialogs (they’re either shown or completely gone)
- Auth gates / role-based content
- Heavy components (charts, maps, large tables)
- Content that should not run code when hidden (timers, event listeners)
-
Use v-show for:
- Tab content / accordions (toggle 100× a day)
- Loading spinners / skeletons
- Tooltips / dropdowns
- Anything where you want instant toggle and state preservation
-
Always use <template v-if> when you want to conditionally render multiple elements without extra wrapper:
vue0123456789<template v-if="isLoggedIn"><p>Welcome</p><button>Logout</button></template> -
Combine v-if + <Transition> for beautiful enter/leave animations
-
Never use v-if on very frequently toggled content — performance hit from mount/unmount cycle
Your mini practice task:
- Create a component with:
- v-if for logged-in vs login form
- v-else-if for loading state
- v-else for guest view
- Add <Transition name=”fade”> around the content
- Toggle login state → watch smooth fade + correct mount/unmount behavior
Any part confusing? Want full examples for:
- v-if + <Transition> + modal animation?
- v-if vs v-show performance comparison?
- v-if inside <TransitionGroup> for list items?
- Real auth guard with v-if + router?
Just tell me — we’ll build the next clean conditional UI together 🚀
