100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
JavaScript

Conditional Rendering: v-if and v-show

Explains Vue's two conditional rendering directives, how they differ at the DOM level, and how to choose the right one for performance and correctness.

Template DirectivesBeginner7 min readJul 9, 2026
Analogies

Conditional Rendering: v-if and v-show

Vue offers two directives for showing or hiding content based on a condition: v-if and v-show. Although they look interchangeable in simple examples, they operate very differently under the hood, and picking the wrong one for a given situation can cause unnecessary re-renders or wasted DOM nodes. v-if controls whether an element (and its component instance, if any) is actually present in the DOM at all, while v-show always keeps the element in the DOM and merely toggles its CSS display property.

🏏

Cricket analogy: v-if is like completely removing a player from the matchday squad sheet when dropped — they're not on the ground at all — while v-show is like benching a player who stays in the dugout, fully present and ready, just not currently on the field of play.

How v-if works

When a v-if condition is falsy, Vue completely destroys the element and any components or directives inside it — event listeners are removed, child component instances are unmounted, and their lifecycle hooks (onUnmounted, etc.) fire. When the condition becomes truthy again, Vue creates a fresh element and, if applicable, a fresh component instance, running mount-time lifecycle hooks again. This makes v-if genuinely expensive to toggle, but cheap to render initially when the condition starts false — nothing is created until needed. v-if also supports v-else-if and v-else for branching between multiple mutually exclusive blocks, and it can be applied to a <template> tag to conditionally group multiple elements without adding an extra wrapper to the DOM.

🏏

Cricket analogy: When a v-if condition goes false, it's like a substitute fielder being sent off the ground entirely, needing the whole warm-up routine again (mount lifecycle hooks) to come back on, which is why constantly rotating players in and out is costly, unlike never fielding them at all if they start on the bench; v-else-if/v-else works like a clear batting order, and it can apply to a whole substitution group (a <template> wrapper) without an extra visible slot.

How v-show works

v-show always renders the element into the DOM regardless of the initial condition, then toggles the inline display: none style whenever the condition changes. The element and any child components stay mounted throughout — their state is preserved, and no lifecycle hooks fire on toggle. This makes v-show cheap to toggle repeatedly but more expensive on first render if the condition is initially false, since the element is created and mounted even though it isn't shown. v-show does not support v-else and cannot be used directly on a <template> element, because it needs a concrete DOM node to apply the style to.

🏏

Cricket analogy: v-show is like a substitute sitting fully padded up in the dugout the entire match, ready to walk straight onto the field instantly, with no lifecycle hooks firing since they never actually left — cheap to send in and pull back repeatedly, but they had to get padded up upfront even if never called on, and there's no 'else' bench slot since v-show just toggles one player's visibility.

vue
<script setup>
import { ref } from 'vue'

const activeTab = ref('profile')
const isPanelOpen = ref(true)
</script>

<template>
  <!-- v-if: branches are mutually exclusive, components fully mount/unmount -->
  <section v-if="activeTab === 'profile'">Profile content</section>
  <section v-else-if="activeTab === 'settings'">Settings content</section>
  <section v-else>No tab selected</section>

  <!-- v-show: toggled frequently, stays in the DOM, just hides visually -->
  <button @click="isPanelOpen = !isPanelOpen">Toggle panel</button>
  <div v-show="isPanelOpen" class="side-panel">
    Panel contents stay mounted even when hidden
  </div>
</template>

This is directly analogous to React's pattern of conditionally returning JSX (equivalent to v-if — nothing renders to the DOM) versus toggling a CSS class or inline style on an always-rendered element (equivalent to v-show). Vue simply bakes both patterns into first-class directives instead of leaving the choice entirely to component logic.

A frequent pitfall is using v-if on content that toggles on every keystroke or scroll event, such as a dropdown that opens and closes rapidly — this repeatedly mounts and unmounts child components, destroying their internal state (like a scroll position or an input's typed text) every time. If content toggles often and preserving internal state matters, v-show is almost always the better choice; reserve v-if for content that is rarely toggled or that should truly not exist in the DOM until needed (e.g., for accessibility or to avoid running expensive child logic).

  • v-if adds/removes the element and its component instance from the DOM entirely; v-show only toggles the CSS display property.
  • v-if triggers real mount/unmount lifecycle hooks each time the condition flips; v-show never does, because the element stays mounted.
  • v-if has a higher toggle cost but a lower initial render cost when the condition starts false.
  • v-show has a higher initial render cost (always mounts) but a much lower toggle cost, making it ideal for frequently toggled UI.
  • v-if supports v-else-if/v-else branching and works on <template> wrapper tags; v-show supports neither.
  • Choose v-if for rarely-toggled or mutually exclusive content, and v-show for UI that toggles often and needs to preserve internal state.

Practice what you learned

Was this page helpful?

Topics covered

#JavaScript#VueJsStudyNotes#WebDevelopment#ConditionalRenderingVIfAndVShow#Conditional#Rendering#Show#Works#StudyNotes#SkillVeris