Tutorial VueJS 3: transiciones y animaciones con Transition
verificado el 2 septiembre 2026 · 5 min
<Transition> anima un elemento único al aparecer y al desaparecer, <TransitionGroup> anima una lista. Vue aplica clases CSS, su hoja de estilos decide el efecto. Atención: en Vue 3, las clases de partida son -enter-from y -leave-from, no -enter y -leave como en Vue 2.
Un elemento que aparece de golpe produce un efecto de salto. Vue incluye dos componentes integrados, <Transition> y <TransitionGroup>, que aplican clases CSS en el momento adecuado para animar las entradas y las salidas. Ninguna biblioteca que instalar.
El principio
Envuelva en <Transition> un elemento cuya visualización dependa de una condición. Vue no lo anima por sí mismo: añade y quita clases, y es su CSS el que decide el efecto.
Seis clases cubren el ciclo completo.
/* entrada */
.v-enter-from /* estado de partida, aplicado justo antes de la inserción */
.v-enter-active /* durante toda la entrada: aquí es donde va transition */
.v-enter-to /* estado de llegada */
/* salida */
.v-leave-from /* estado de partida de la salida */
.v-leave-active /* durante toda la salida */
.v-leave-to /* estado final, justo antes de retirarlo del DOM */El prefijo v- es el de una transición sin nombre. Con name="fade", las clases pasan a ser fade-enter-from, fade-enter-active, y así sucesivamente.
En Vue 2, las clases de partida se llamaban .fade-enter y .fade-leave, sin sufijo. Vue 3 las renombró como -enter-from y -leave-from. Un ejemplo copiado de un artículo antiguo no producirá, por tanto, ninguna animación de entrada, sin el menor mensaje de error. Si su transición no hace nada, compruebe primero este punto.
Animar la disponibilidad
En ProductDisplay.vue, el mensaje de stock alterna entre dos párrafos según la variante sobrevolada. Envuélvalos:
<Transition name="fade" mode="out-in">
<p v-if="inStock" key="in">En stock</p>
<p v-else key="out">En rupture</p>
</Transition>Después describa el efecto en la hoja de estilos, como en el capítulo sobre las clases y los estilos:
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .3s ease, transform .3s ease;
}Pase el ratón por los círculos de color: el mensaje se funde en lugar de saltar.
mode="out-in" espera a que el elemento antiguo haya salido antes de insertar el nuevo. Sin él, los dos párrafos coexisten una fracción de segundo y se superponen.
Lo que acepta Transition
Un solo elemento a la vez. Es la restricción principal: <Transition> anima un hijo único, no una lista.
Se activa en tres situaciones: un v-if, un v-show o un cambio de componente mediante <component :is>. Para forzar una transición entre dos elementos del mismo tipo, deles key diferentes, como más arriba.
Animar una lista
Para una lista hace falta <TransitionGroup>. Dos diferencias: acepta varios hijos, y renderiza él mismo un elemento contenedor si se lo pide con tag.
En ReviewList.vue, sustituya el <ul>:
<template>
<div class="review-container">
<h3>Avis</h3>
<TransitionGroup name="liste" tag="ul">
<li v-for="review in reviews" :key="review.id">
{{ review.name }} a mis {{ review.rating }} étoiles
<br>
« {{ review.review }} »
</li>
</TransitionGroup>
</div>
</template>Cada hijo necesita una key única y estable. El índice no sirve aquí: cambia en cada inserción, y Vue ya no sabría qué elemento es nuevo. Demos un identificador a cada opinión en el momento de añadirla:
<script setup>
function addReview(review) {
reviews.value.push({ ...review, id: crypto.randomUUID() })
}
</script>crypto.randomUUID() está disponible en todos los navegadores actualizados, en las páginas servidas por HTTPS o desde localhost.
El CSS añade una clase más, -move, que anima el desplazamiento de los elementos restantes cuando un vecino desaparece:
.liste-enter-from,
.liste-leave-to {
opacity: 0;
transform: translateX(24px);
}
.liste-enter-active,
.liste-leave-active,
.liste-move {
transition: all .3s ease;
}Envíe varias opiniones: cada una entra deslizándose, y las demás se reorganizan con suavidad.
Para que -move funcione al eliminar, el elemento que se va debe salir del flujo, si no, los demás solo se mueven al final de su animación. Añada position: absolute a la clase -leave-active cuando sus elementos puedan eliminarse.
Animar la primera visualización
Por defecto, nada se anima en el renderizado inicial. El atributo appear cambia eso:
<Transition name="fade" appear>
<p>Visible dès le chargement, en fondu</p>
</Transition>Las animaciones CSS y los hooks
transition no es la única opción: una animation CSS también funciona, aplicada sobre la clase -enter-active.
Para controlar una biblioteca de animación en JavaScript, <Transition> emite eventos en cada etapa:
<Transition
:css="false"
@before-enter="avantEntree"
@enter="entree"
@after-enter="apresEntree"
@leave="sortie"
>
<p v-if="visible">Contenu</p>
</Transition>Con :css="false", Vue deja de gestionar las clases y le cede el control. Las funciones enter y leave reciben entonces el elemento y un callback done que hay que llamar cuando la animación termina:
function entree(el, done) {
// animación con la biblioteca que prefiera
done()
}El movimiento no siempre es deseable
Algunas personas desactivan las animaciones a nivel del sistema, en particular para evitar las náuseas provocadas por el movimiento. Respete ese ajuste:
@media (prefers-reduced-motion: reduce) {
.fade-enter-active,
.fade-leave-active,
.liste-enter-active,
.liste-leave-active,
.liste-move {
transition: none;
}
}La interfaz sigue siendo funcional, solo desaparece la animación.
Es el final del tutorial
Su aplicación muestra un producto y sus variantes, gestiona un carrito, valida un formulario y anima sus cambios de estado. Ha cubierto la reactividad, las directivas, los componentes, las props, los eventos y las transiciones: suficiente para leer y escribir Vue a diario.
Para continuar, dos direcciones. Compartir estado entre componentes alejados con Pinia, cuando propagar eventos hacia arriba se vuelve pesado. Y el enrutamiento con Vue Router, en cuanto la aplicación tiene varias páginas.
<Transition> y <TransitionGroup> son componentes integrados: se escriben exactamente igual. Solo las funciones de los hooks JavaScript cambian de sitio, a la opción methods.
key ligada a la variante seleccionada.Errores frecuentes
.fade-enter y .fade-leave se renombraron como -enter-from y -leave-from. Nada se anima y no aparece ningún error.global: { stubs: { 'transition-group': false } } para observar el renderizado real.