Chapter 83: Vue v-show Directive
V-show
This directive is very similar to v-if, but it solves the same problem in a completely different way, and knowing when to choose v-show over v-if (and vice versa) is one of the things that separates good Vue code from great Vue code.
1. What does v-show actually do? (Very clear mental model)
v-show tells Vue:
“Keep this element (and everything inside it) in the DOM always, but toggle its visibility by changing the CSS property display: none / display: block (or similar) depending on whether the expression is true or false.”
In other words:
- v-show=”true” → element is visible (display = its normal value)
- v-show=”false” → element is hidden (display: none)
But crucially:
The element is never removed from the DOM — it just becomes invisible.
2. v-show vs v-if – The Decision Table You Must Memorize
| Feature / Question | v-show | v-if | Winner when… |
|---|---|---|---|
| DOM presence when condition = false | Still exists (just display: none) | Completely removed from DOM | — |
| Cost when toggling from false → true | Very cheap (just CSS change) | Expensive (full mount + setup + render) | Frequent toggle → v-show |
| Cost when toggling from true → false | Very cheap (just CSS change) | Expensive (full unmount + cleanup) | Frequent toggle → v-show |
| Lifecycle hooks on toggle | Never runs mounted/unmounted again | Runs mounted/unmounted every time | Need lifecycle reset → v-if |
| Initial render performance | Always renders (even when hidden) | Only renders when true | Heavy content + usually hidden → v-if |
| Animation support | Works, but element always exists | Perfect with <Transition> (true enter/leave) | Both good, v-if more natural |
| Best for | Things that toggle very often | Things that toggle rarely or are expensive | — |
| Typical real-world examples | Tabs content, accordions, loading spinners, tooltips, dark-mode sections | Modals, auth gates, role-based panels, payment status blocks, heavy charts/maps | — |
Golden rule of thumb (2026)
- Use v-show when the condition changes frequently (many times per minute) or you want instant toggle speed and state preservation (form values, scroll position, internal counters…)
- Use v-if when the condition changes rarely or the content is expensive (large lists, complex components, third-party widgets) or you want true cleanup (stop timers, remove listeners, free memory)
3. Real, Practical Example – Tabbed Interface (Classic v-show use-case)
|
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 |
<template> <div class="tabs-demo"> <div class="tab-buttons"> <button v-for="tab in tabs" :key="tab.name" :class="{ active: activeTab === tab.name }" @click="activeTab = tab.name" > {{ tab.label }} </button> </div> <!-- v-show: tabs toggle very often → we want instant switch + state preservation --> <div v-show="activeTab === 'overview'" class="tab-content"> <h2>Overview Tab</h2> <input v-model="overviewNote" placeholder="Take notes here..." /> <p>Notes persist when switching tabs → because component never unmounts</p> </div> <div v-show="activeTab === 'settings'" class="tab-content"> <h2>Settings Tab</h2> <label> <input type="checkbox" v-model="darkMode" /> Dark Mode </label> <p>Checkbox state is remembered → no re-mount</p> </div> <div v-show="activeTab === 'profile'" class="tab-content"> <h2>Profile Tab</h2> <p>Scroll down → position preserved on tab switch</p> <div class="long-content"> <p v-for="n in 50" :key="n">Line {{ n }}</p> </div> </div> </div> </template> <script setup lang="ts"> import { ref } from 'vue' const activeTab = ref('overview') const overviewNote = ref('') const darkMode = ref(false) const tabs = [ { name: 'overview', label: 'Overview' }, { name: 'settings', label: 'Settings' }, { name: 'profile', label: 'Profile' } ] </script> <style scoped> .tabs-demo { max-width: 800px; margin: 3rem auto; } .tab-buttons { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; } .tab-buttons button { padding: 0.8rem 1.5rem; border: 1px solid #ccc; border-radius: 6px; background: white; cursor: pointer; } .tab-buttons .active { background: #3b82f6; color: white; border-color: #3b82f6; } .tab-content { padding: 1.5rem; background: #f8fafc; border-radius: 8px; min-height: 300px; } .long-content { height: 400px; overflow-y: auto; border: 1px solid #eee; padding: 1rem; } </style> |
What happens here
- Switch tabs 20 times → instant (no mount/unmount cost)
- Notes in Overview tab → remembered (input value preserved)
- Scroll position in Profile tab → preserved
- Checkbox in Settings tab → state stays
- No unnecessary DOM removals/additions → very fast & smooth
Compare with v-if (try changing v-show to v-if):
→ Every tab switch → component unmounts & remounts → notes lost, scroll reset, checkbox reset → bad UX
4. Quick Summary Table – v-show in 2026
| Question | v-show Behavior | When to choose v-show over v-if |
|---|---|---|
| DOM presence when false | Still exists (display: none) | You need instant toggle |
| Cost when toggling | Extremely cheap (CSS change only) | Frequent toggles (tabs, accordions) |
| Lifecycle hooks on toggle | Never runs again | You want state preserved |
| Animation support | Works, but element always present | Quick visibility toggles |
| Initial render | Always renders (even when hidden) | Content is cheap/lightweight |
| Best for | Tabs, accordions, loading spinners, tooltips, dark-mode sections | — |
Pro Tips from Real Projects (Hyderabad 2026)
- Use v-show for anything that toggles more than ~5–10 times per session (tabs, dropdowns, accordions, mobile menus, loading overlays)
- Use v-if for anything expensive or rarely shown (modals, auth walls, role-based dashboards, payment status blocks)
- Combine v-show + <KeepAlive> when you want to cache very expensive tab content
- For accessibility → v-show elements are still in DOM → screen readers see them (use aria-hidden when needed)
- Test on low-end mobile — v-show toggle is almost free, v-if can feel laggy if content is heavy
Your mini practice task:
- Build the tabbed interface above using v-show
- Add an input + checkbox in each tab → switch tabs 10 times → see state preserved
- Change all v-show to v-if → switch again → see state lost (notes cleared, scroll reset)
- Add <Transition name=”fade”> around tab content → see smooth visibility change
Any part confusing? Want full examples for:
- v-show + <Transition> + tab content fade?
- v-show vs v-if performance comparison (with heavy component)?
- v-show inside <TransitionGroup> for list items?
- Real mobile menu / accordion with v-show?
Just tell me — we’ll build the next smooth, preserved-state tab system together 🚀
