Conditional Rendering - v-if and v-show
Showing and hiding elements with v-if, v-else, and v-show, and the real difference between them.
3 min de lecture
Vue has two directives for showing or hiding content based on a condition, and they work in genuinely different ways — picking the right one for a given situation matters more than it might first appear.
v-if, v-else-if, v-else
<script setup>
import { ref } from "vue";
const status = ref("loading");
</script>
<template>
<p v-if="status === 'loading'">Loading...</p>
<p v-else-if="status === 'error'">Something went wrong.</p>
<p v-else>Here's your data.</p>
</template>v-if adds or removes the element (and everything inside it) from the DOM entirely, based on whether the expression is truthy. v-else-if and v-else must immediately follow a v-if (or another v-else-if) with no other elements in between, forming a single conditional chain — much like a JavaScript if/else if/else.
You can also apply v-if to a <template> tag when you need to condition a group of elements without introducing an extra wrapping element in the actual DOM:
<template>
<template v-if="status === 'loading'">
<p>Loading...</p>
<spinner-icon />
</template>
</template>v-show: always in the DOM, just hidden
<script setup>
import { ref } from "vue";
const isVisible = ref(true);
</script>
<template>
<p v-show="isVisible">This toggles with CSS, not by leaving the DOM.</p>
</template>v-show renders the element unconditionally and toggles its visibility with plain display: none when the condition is false. Unlike v-if, the element (and its component's internal state) never actually leaves the DOM — it's just visually hidden.
Which one to use, and why
The difference matters most around cost and state:
v-ifhas a higher toggle cost, since Vue has to create or destroy real DOM nodes (and mount/unmount any component inside) every time the condition flips. It has a lower initial render cost if the condition starts false, since nothing gets created at all.v-showhas a near-zero toggle cost — it's just a CSS property flip — but pays the full render cost upfront regardless of the initial condition, since the element is always created.
A good rule of thumb: use v-show for something that toggles frequently (a dropdown menu opening and closing, a tab panel), and v-if for something that's conditionally rendered once and rarely changes (an error message that appears once a request fails, a feature gated behind a permission check).
There's a second, easy-to-miss difference: v-if genuinely destroys the element, which resets any internal state a child component held (an input's typed text, a form's local state). v-show never does — the element and its component stay alive and remember their state the whole time, since they were never actually removed.
<template>
<!-- Typing here, then toggling this off and on, clears the input -->
<input v-if="showInput" />
<!-- Typing here survives toggling, since the input never unmounts -->
<input v-show="showInput" />
</template>Note v-if and v-for should never be used on the same element — Vue's own style guide flags it, since v-for's per-item variables aren't reliably available to a v-if on the same tag depending on precedence version to version. Wrap one of them in a <template> instead if you need both.