Tutoriel VueJS 3 – Transitions
vérifié le 2 septembre 2026 · 5 min
<Transition> anime un élément unique à son apparition et à sa disparition, <TransitionGroup> anime une liste. Vue applique des classes CSS, votre feuille de style décide de l’effet. Attention : en Vue 3, les classes de départ sont -enter-from et -leave-from, pas -enter et -leave comme en Vue 2.
Un élément qui apparaît d’un coup produit un effet de saut. Vue fournit deux composants intégrés, <Transition> et <TransitionGroup>, qui posent des classes CSS au bon moment pour animer les entrées et les sorties. Aucune bibliothèque à installer.
Le principe
Enveloppez dans <Transition> un élément dont l’affichage dépend d’une condition. Vue ne l’anime pas lui-même : il ajoute et retire des classes, et c’est votre CSS qui décide de l’effet.
Six classes couvrent le cycle complet.
/* entrée */
.v-enter-from /* état de départ, appliqué juste avant l’insertion */
.v-enter-active /* pendant toute l’entrée : c’est ici que va transition */
.v-enter-to /* état d’arrivée */
/* sortie */
.v-leave-from /* état de départ de la sortie */
.v-leave-active /* pendant toute la sortie */
.v-leave-to /* état final, juste avant le retrait du DOM */Le préfixe v- est celui d’une transition sans nom. Avec name="fade", les classes deviennent fade-enter-from, fade-enter-active, et ainsi de suite.
En Vue 2, les classes de départ s’appelaient .fade-enter et .fade-leave, sans suffixe. Vue 3 les a renommées en -enter-from et -leave-from. Un exemple copié d’un article ancien ne produira donc aucune animation à l’entrée, sans le moindre message d’erreur. Si votre transition ne fait rien, vérifiez d’abord ce point.
Animer la disponibilité
Dans ProductDisplay.vue, le message de stock bascule entre deux paragraphes selon la déclinaison survolée. Enveloppez-les :
<Transition name="fade" mode="out-in">
<p v-if="inStock" key="in">En stock</p>
<p v-else key="out">En rupture</p>
</Transition>Puis décrivez l’effet dans la feuille de style, comme au chapitre sur les classes et les styles :
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .3s ease, transform .3s ease;
}Survolez les pastilles : le message se fond au lieu de sauter.
mode="out-in" attend que l’ancien élément soit sorti avant d’insérer le nouveau. Sans lui, les deux paragraphes coexistent une fraction de seconde et se chevauchent.
Ce que Transition accepte
Un seul élément à la fois. C’est la contrainte principale : <Transition> anime un enfant unique, pas une liste.
Il se déclenche sur trois situations : un v-if, un v-show, ou un changement de composant via <component :is>. Pour forcer une transition entre deux éléments de même type, donnez-leur des key différentes, comme ci-dessus.
Animer une liste
Pour une liste, il faut <TransitionGroup>. Deux différences : il accepte plusieurs enfants, et il rend lui-même un élément conteneur si vous le lui demandez avec tag.
Dans ReviewList.vue, remplacez le <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>Chaque enfant a besoin d’une key unique et stable. L’index ne convient pas ici : il change à chaque insertion, et Vue ne saurait plus quel élément est nouveau. Donnons un identifiant à chaque avis au moment de l’ajout :
<script setup>
function addReview(review) {
reviews.value.push({ ...review, id: crypto.randomUUID() })
}
</script>crypto.randomUUID() est disponible dans tous les navigateurs à jour, sur les pages servies en HTTPS ou depuis localhost.
Le CSS ajoute une classe de plus, -move, qui anime le déplacement des éléments restants quand un voisin disparaît :
.liste-enter-from,
.liste-leave-to {
opacity: 0;
transform: translateX(24px);
}
.liste-enter-active,
.liste-leave-active,
.liste-move {
transition: all .3s ease;
}Déposez plusieurs avis : chacun entre en glissant, et les autres se réorganisent en douceur.
Pour que -move fonctionne à la suppression, l’élément qui part doit être retiré du flux, sinon les autres ne bougent qu’à la fin de son animation. Ajoutez position: absolute à la classe -leave-active quand vos éléments sont supprimables.
Animer au premier affichage
Par défaut, rien n’est animé au rendu initial. L’attribut appear change cela :
<Transition name="fade" appear>
<p>Visible dès le chargement, en fondu</p>
</Transition>Les animations CSS et les crochets
transition n’est pas la seule option : une animation CSS fonctionne aussi, appliquée sur la classe -enter-active.
Pour piloter une bibliothèque d’animation en JavaScript, <Transition> émet des événements à chaque étape :
<Transition
:css="false"
@before-enter="avantEntree"
@enter="entree"
@after-enter="apresEntree"
@leave="sortie"
>
<p v-if="visible">Contenu</p>
</Transition>Avec :css="false", Vue cesse de gérer les classes et vous laisse la main. Les fonctions enter et leave reçoivent alors l’élément et un rappel done à appeler quand l’animation est finie :
function entree(el, done) {
// animation via la bibliothèque de votre choix
done()
}Le mouvement n’est pas toujours souhaitable
Certaines personnes désactivent les animations au niveau du système, notamment pour éviter les nausées liées au mouvement. Respectez ce réglage :
@media (prefers-reduced-motion: reduce) {
.fade-enter-active,
.fade-leave-active,
.liste-enter-active,
.liste-leave-active,
.liste-move {
transition: none;
}
}L’interface reste fonctionnelle, seule l’animation disparaît.
C’est la fin du tutoriel
Votre application affiche un produit et ses déclinaisons, gère un panier, valide un formulaire et anime ses changements d’état. Vous avez couvert la réactivité, les directives, les composants, les props, les événements et les transitions : de quoi lire et écrire du Vue au quotidien.
Pour la suite, deux directions. Le partage d’état entre composants éloignés avec Pinia, quand la remontée d’événements devient pénible. Et le routage avec Vue Router, dès que l’application a plusieurs pages.
<Transition> et <TransitionGroup> sont des composants intégrés : ils s’écrivent exactement pareil. Seules les fonctions des crochets JavaScript changent de place, dans l’option methods.
key liée à la déclinaison sélectionnée.Erreurs fréquentes
.fade-enter et .fade-leave ont été renommées en -enter-from et -leave-from. Rien ne s’anime et aucune erreur n’apparaît.global: { stubs: { 'transition-group': false } } pour observer le vrai rendu.