Chapitre 11 sur 11

Tutoriel VueJS 3 – Transitions

vérifié le 2 septembre 2026 · 5 min

Réponse rapide

<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.

À la fin de ce chapitre, vous saurez animer l’apparition, la disparition et le déplacement d’éléments, sans bibliothèque.

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.

css
/* 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.

Le piège du code trouvé sur le web

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 :

src/components/ProductDisplay.vue
<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 :

src/assets/boutique.css
.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> :

src/components/ReviewList.vue
<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 :

src/components/ProductDisplay.vue
<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 :

src/assets/boutique.css
.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.

Sortie et déplacement

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 :

html
<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 :

html
<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 :

javascript
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 :

src/assets/boutique.css
@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.

En Options API

<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.

ExerciceAjoutez une transition sur l’image du produit pour qu’elle se fonde au changement de déclinaison. Vous aurez besoin d’une key liée à la déclinaison sélectionnée.

Erreurs fréquentes

Utiliser les classes de Vue 2 .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.
Oublier mode="out-in" Sans lui, l’élément entrant et l’élément sortant coexistent une fraction de seconde et se chevauchent.
Utiliser l’index comme clé dans un TransitionGroup Vue ne peut plus distinguer l’élément nouveau des autres et anime la mauvaise entrée.
Tester un composant animé avec Vue Test Utils Les transitions sont remplacées par des stubs par défaut. Passez global: { stubs: { 'transition-group': false } } pour observer le vrai rendu.
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.