v-if, v-else y v-show en Vue: cuándo usar cada una
Las directivas v-if, v-else y v-show sirven para mostrar u ocultar contenido en función de una condición reactiva.
La diferencia clave —la que evita bugs sutiles y problemas de rendimiento— es esta:
v-if/v-else: crean o destruyen elementos del DOM.v-show: no destruye nada; solo alternadisplay: none.
Si lo piensas como “¿Esto existe?” vs “¿Esto se ve?”, ya vas por buen camino.
Qué hace cada una
v-if
Renderiza el elemento solo si la condición es true.
Si es false, el elemento no existe en el DOM.
Útil cuando:
- El contenido es pesado (componentes grandes, listas, gráficos)
- No debería existir si no se cumple la condición (permisos de UI, flujos sensibles)
- Se muestra pocas veces u ocasionalmente
v-iftiene un costo mayor al alternar, porque implica montaje y desmontaje del árbol de componentes.
v-else y v-else-if
Se usan inmediatamente después de un v-if o v-else-if.
Reglas importantes:
- Deben estar adyacentes (sin nodos intermedios).
- Vue los interpreta como un único bloque condicional.
v-show
El elemento siempre se renderiza (existe en el DOM), pero se muestra u oculta con CSS.
Útil cuando:
- La visibilidad cambia con frecuencia (toggles, menús, paneles)
- Quieres evitar el costo de montar/desmontar
v-showno funciona con<template>, porque no es un elemento real. El contenido sigue en el DOM: foco, tab order y accesibilidad pueden verse afectados si no se gestiona bien.
Resumen rápido: cuál usar
- Usa
v-ifcuando el contenido no debe existir si la condición no se cumple. - Usa
v-showcuando el contenido solo debe ocultarse y el toggle será frecuente. - Usa
v-elsepara el caso alternativo inmediato de unv-if.
Ejemplo 1: Login (v-if + v-else)
Cuando una persona está autenticada, muestras el panel; si no, un CTA para iniciar sesión.
<script setup>
import { ref } from "vue";
const isLoggedIn = ref(false);
function toggleLogin() {
isLoggedIn.value = !isLoggedIn.value;
}
</script>
<template>
<button @click="toggleLogin">
{{ isLoggedIn ? "Log out" : "Log in" }}
</button>
<section v-if="isLoggedIn">
<h2>Welcome back</h2>
<p>You have access to your dashboard.</p>
</section>
<section v-else>
<h2>Please log in</h2>
<p>You need an account to continue.</p>
</section>
</template><script>
export default {
data() {
return {
isLoggedIn: false,
};
},
methods: {
toggleLogin() {
this.isLoggedIn = !this.isLoggedIn;
},
},
};
</script>
<template>
<button @click="toggleLogin">
{{ isLoggedIn ? "Log out" : "Log in" }}
</button>
<section v-if="isLoggedIn">
<h2>Welcome back</h2>
<p>You have access to your dashboard.</p>
</section>
<section v-else>
<h2>Please log in</h2>
<p>You need an account to continue.</p>
</section>
</template>El
<section v-else>debe ir justo después delv-if. Si intercalas un comentario, un<div>u otro nodo, Vue deja de asociarlo comoelse.
Ejemplo 2: Tabs o panel que se abre y cierra (v-show)
Un panel de filtros que el usuario abre y cierra constantemente. Aquí v-show es ideal.
<script setup>
import { ref } from "vue";
const isOpen = ref(false);
</script>
<template>
<button @click="isOpen = !isOpen">
{{ isOpen ? "Hide filters" : "Show filters" }}
</button>
<aside v-show="isOpen" class="filters">
<h3>Filters</h3>
<label>
<input type="checkbox" />
Only available items
</label>
</aside>
</template><script>
export default {
data() {
return {
isOpen: false,
};
},
};
</script>
<template>
<button @click="isOpen = !isOpen">
{{ isOpen ? "Hide filters" : "Show filters" }}
</button>
<aside v-show="isOpen" class="filters">
<h3>Filters</h3>
<label>
<input type="checkbox" />
Only available items
</label>
</aside>
</template>Nota de UX y accesibilidad
Como el panel sigue existiendo, sus elementos interactivos pueden:
- Conservar estado
- Conservar foco (a veces deseable, a veces no)
- Seguir siendo accesibles para lectores de pantalla si no se manejan roles/ARIA
Si el contenido no debería existir en absoluto, usa v-if.
v-else-if: múltiples estados (sin anidar if innecesarios)
Un patrón común: loading, error y success.
<script setup>
import { ref } from "vue";
const status = ref("idle"); // "idle" | "loading" | "error" | "success"
</script>
<template>
<button @click="status = 'loading'">Simulate loading</button>
<button @click="status = 'error'">Simulate error</button>
<button @click="status = 'success'">Simulate success</button>
<button @click="status = 'idle'">Reset</button>
<p v-if="status === 'loading'">Loading...</p>
<p v-else-if="status === 'error'">Something went wrong.</p>
<p v-else-if="status === 'success'">Done!</p>
<p v-else>Idle. Click a button.</p>
</template><script>
export default {
data() {
return {
status: "idle", // "idle" | "loading" | "error" | "success"
};
},
};
</script>
<template>
<button @click="status = 'loading'">Simulate loading</button>
<button @click="status = 'error'">Simulate error</button>
<button @click="status = 'success'">Simulate success</button>
<button @click="status = 'idle'">Reset</button>
<p v-if="status === 'loading'">Loading...</p>
<p v-else-if="status === 'error'">Something went wrong.</p>
<p v-else-if="status === 'success'">Done!</p>
<p v-else>Idle. Click a button.</p>
</template>Errores comunes
1) v-else separado del v-if
Incorrecto:
<div v-if="ok">Ok</div>
<!-- comentario o nodo -->
<div v-else>No ok</div>El
v-elsedebe ir inmediatamente después delv-if.
2) Usar v-show para contenido que no debería existir
Si ocultas con v-show algo como un admin panel, sigue en el DOM.
No es seguridad; es solo presentación. La seguridad va en el backend, pero en la UI al menos usa v-if.
3) Pensar que v-if es “gratis”
Alternar v-if muchas veces implica montajes y desmontajes repetidos.
Si el usuario va a abrir y cerrar algo constantemente, v-show suele ser mejor opción.
Conclusión
v-if: controla la existencia real del elemento.v-else / v-else-if: ramas alternativas del mismo bloque condicional.v-show: ocultar/mostrar rápido con CSS, sin destruir el DOM.
Una UI se vuelve más clara cuando decides conscientemente: ¿Quiero que esto exista o solo que se vea?
