Vue.js: Transitions & Animations

Last updated: 2026-10-20

Transitions and animations make your Vue app feel more alive—elements enter and leave smoothly, and lists reorder gracefully when they change. Vue ships with the built-in <Transition> and <TransitionGroup> components, which work with CSS class names or JavaScript hooks to produce a wide range of animation effects.

Mastering transitions is the key to a better user experience—good animation makes an app feel "fluid, responsive, and professional."

Related Directives:v-if

1. What You'll Learn


2. The Awkward "Hard-Cut Tab Switch"

(1) Pain Point: Components Vanish and Reappear, the UI Flickers

Alice's admin dashboard has a tab switcher:

User experience

(2) The Vue Transition Fix: 1 Tag, 6 Classes

User experience

(3) Benefits

After adding the transition:


3. Single-Element Transition

(1) Basic Usage

(2) The 6 CSS Classes Explained

Class When it fires Purpose
v-enter-from Before the element is inserted Starting state
v-enter-active Throughout the insert Transition in progress
v-enter-to After the element is inserted Ending state
v-leave-from Before the element leaves Starting state
v-leave-active Throughout the leave Transition in progress
v-leave-to After the element leaves Ending state

(3) Naming Rules

VUE
<!-- v-bind:name="fade" → the 6 classes are .fade-xxx -->
<Transition name="fade">...</Transition>

<!-- No name → defaults to v-xxx (the 6 classes are .v-xxx) -->
<Transition>...</Transition>

<!-- Dynamic name -->
<Transition :name="transitionName">...</Transition>

(4) The 3 Modes

VUE
<!-- 1. Default mode: enter and leave happen at the same time (overlap) -->
<Transition name="fade">
  <div v-if="show">Content</div>
</Transition>

<!-- 2. out-in: leave first, then enter (recommended, avoids overlap) -->
<Transition name="fade" mode="out-in">
  <div v-if="show">Content</div>
</Transition>

<!-- 3. in-out: enter first, then leave (rarely used) -->
<Transition name="fade" mode="in-out">
  <div v-if="show">Content</div>
</Transition>

▶ Example: Default Mode vs mode="out-in", Side by Side

The same pair of v-if cards twice: the top one with no mode (enter and leave overlap), the bottom one with mode="out-in" (leave finishes before enter starts). Toggle and the difference is obvious.

▶ Try it Yourself

Output:

TEXT
Both areas start on the green "Card A", and the button reads "Switch card (current A)".
After one click, the top area fades the old card out and the new one in at the same time — green and blue briefly overlap and the container height jumps.
The bottom `mode="out-in"` area waits ~0.5s for the green card to leave, then fades the blue "Card B" in; only one card is ever visible.
Clicking again switches back to A with the same visible difference.

4. 5 CSS Animations

(1) Fade (most common)

CSS
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}

(2) Slide

CSS
.slide-enter-active, .slide-leave-active {
  transition: transform 0.3s;
}
.slide-enter-from {
  transform: translateX(-100%);
}
.slide-leave-to {
  transform: translateX(100%);
}

(3) Scale

CSS
.scale-enter-active, .scale-leave-active {
  transition: transform 0.3s;
}
.scale-enter-from, .scale-leave-to {
  transform: scale(0);
}

(4) Rotate

CSS
.rotate-enter-active {
  transition: transform 1s;
}
.rotate-enter-from {
  transform: rotate(0deg);
}
.rotate-leave-active {
  animation: bounce 0.5s;
}
@keyframes bounce {
  0% { transform: scale(0); }
  50% { transform: scale(1.2); }
  100% { transform: scale(1); }
}

(5) Combined

CSS
.combo-enter-active, .combo-leave-active {
  transition: all 0.3s;
}
.combo-enter-from {
  opacity: 0;
  transform: translateX(-50px) scale(0.8);
}
.combo-leave-to {
  opacity: 0;
  transform: translateX(50px) scale(0.8);
}

5. TransitionGroup List Animations

(1) Basic Usage

(2) Key Point: .list-move

CSS
/* Applied automatically when an item changes position (e.g. reordering) */
.list-move {
  transition: transform 0.5s;
}

(3) 5 List Animation Scenarios

Scenario Animation
Adding an item Fade in + slide down
Removing an item Fade out + slide left
Reordering Smooth move to the new position
Filtering Leaving items fade out, remaining items transition smoothly
Dragging Follows the mouse in real time

▶ Example: Adding, Removing, and Reordering with TransitionGroup

"Add item" shows the enter animation, clicking a row's × shows the leave animation with the other rows sliding up, and "Move first to bottom" shows .list-move sliding whole rows.

▶ Try it Yourself

Output:

TEXT
The list starts with 3 rows: Vue / Router / Pinia, each with a red × on the right.
"Add an item" fades the new row "Item 4" in and slides it from the right (0.5s); clicking again appends "Item 5".
Clicking a row's × fades that row out while the remaining rows slide up smoothly within 0.5s instead of jumping.
"Move first to bottom" glides the first row down to the end while the others shift up — a continuous motion, no teleporting.

6. JavaScript Hooks

(1) The 8 JS Hooks

VUE
<template>
  <Transition
    @before-enter="beforeEnter"
    @enter="enter"
    @after-enter="afterEnter"
    @before-leave="beforeLeave"
    @leave="leave"
    @after-leave="afterLeave"
    :css="false"  <!-- disable CSS classes -->
  >
    <p v-if="show">Hello</p>
  </Transition>
</template>

<script setup>
import gsap from 'gsap'

function beforeEnter(el) {
  console.log('Before enter:', el)
}

function enter(el, done) {
  gsap.to(el, {
    opacity: 1,
    y: 0,
    duration: 0.5,
    onComplete: done  // signal that the transition is done
  })
}

function afterEnter(el) {
  console.log('After enter:', el)
}

// leave / beforeLeave / afterLeave work the same way
</script>
BASH
npm install gsap
VUE
<template>
  <Transition :css="false" @enter="onEnter" @leave="onLeave">
    <p v-if="show">Hello GSAP</p>
  </Transition>
</template>

<script setup>
import gsap from 'gsap'

function onEnter(el, done) {
  gsap.fromTo(el, 
    { opacity: 0, y: 50 },
    { opacity: 1, y: 0, duration: 0.5, onComplete: done }
  )
}

function onLeave(el, done) {
  gsap.to(el, { opacity: 0, y: -50, duration: 0.3, onComplete: done })
}
</script>

7. Third-Party Animation Libraries

(1) Animate.css Integration

BASH
npm install animate.css
JS
// main.js
import 'animate.css'

(2) @vueuse/motion

BASH
npm install @vueuse/motion
JS
// main.js
import { MotionPlugin } from '@vueuse/motion'
app.use(MotionPlugin)

(3) When to Use GSAP

Library Rating Best for
CSS animations ⭐⭐⭐⭐⭐ Simple transitions, fades
Animate.css ⭐⭐⭐⭐ A large set of preset animations
@vueuse/motion ⭐⭐⭐⭐ Declarative, well integrated with Vue
GSAP ⭐⭐⭐⭐⭐ Complex timelines, physics-based animation

8. Putting It Together: All 5 Transitions in One Demo

▶ Example: 5 Vue Transition Animations (fully supported via CDN)

▶ Expand runnable example (87 lines)
▶ Try it Yourself

Output:

TEXT
Five demo blocks render stacked down the page.
Clicking a button starts a 0.3s animation instead of an instant swap: the greeting fades out, the slide paragraph moves off to the right edge, the modal scales up from 0 over a dark backdrop, added list items fade in and shifted items glide to their new position, and the tab panel uses the combo animation because of mode="out-in".
TEXT
┌────────────────────────────────────────────────┐
│ 1. Fade in / fade out                          │
│   [Hide]   👋 Hello Fade                        │
│ 2. Slide                                       │
│   👉 Slide in                                   │
│   [Toggle]                                     │
│ 3. Scale (modal)                               │
│   [Open modal]                                 │
│ 4. TransitionGroup list animation              │
│   [New item        ] [Add]                     │
│   • Item 1   ×                                 │
│   • Item 2   ×                                 │
│ 5. Combo (tab switching)                       │
│   [Home] [Profile] [Settings]                  │
│   Current tab: Home                            │
└────────────────────────────────────────────────┘

Each interaction animates over 0.3s (0.5s for list items)
instead of switching instantly.

(1) 📊 The 6 CSS Classes in Detail

Class Trigger
.fade-enter-from Before the element is inserted
.fade-enter-active During the insert
.fade-enter-to After the element is inserted
.fade-leave-from Before the element leaves
.fade-leave-active During the leave
.fade-leave-to After the element leaves

(2) 📊 5 Major Third-Party Animation Libraries (⚠️ requires Vite)

Library Command Best for
CSS None (built in) Simple cases
Animate.css npm i animate.css Preset animations
@vueuse/motion npm i @vueuse/motion Declarative usage
GSAP npm i gsap Complex timelines
Motion One npm i motion Lightweight web animation

(3) 📊 Quick Reference for 5 Common Mistakes

Mistake Symptom Fix
Transition has no effect The element just disappears Wrap it in <Transition> together with v-if
v-for items don't animate List items have no transition Use <TransitionGroup>
Reordering isn't animated Items jump abruptly Add the .list-move class
Several components switch at once They overlap Add a distinct key + mode="out-in"
CSS animation too fast/slow Poor experience Tune the transition duration to 0.2–0.5s

(4) 📊 5 Real-World Scenarios

Scenario Animation
Tab switching fade + out-in
Modal dialog scale + fade
List add/remove list animation
Route changes fade + slide
Toast notifications slide from top + auto-dismiss

Related Directives:v-if

❓ FAQ

Q Should I use Transition or CSS @keyframes?
A For simple transitions (opacity/transform), use a CSS transition. For complex animation (multi-step, physics), use @keyframes or GSAP. Vue's Transition supports both.
Q Does TransitionGroup require :key?
A Yes. Every child of <TransitionGroup> must have a unique :key, otherwise the animation won't fire.
Q How do I animate "page switches"?
A Wrap the content in <Transition> using <router-view v-slot="{ Component }">. Each route change swaps the Component, which triggers the Transition.
Q What does mode="out-in" do?
A It lets the leaving component finish its animation before the entering one starts, so the two never show at the same time (no overlap).
Q Can I mix JS hooks with CSS classes?
A Yes. <Transition :css="false" @enter="onEnter"> disables the CSS classes and relies only on JS hooks. Or enable both (CSS classes for styling, JS hooks for callbacks).
Q What if animation performance is poor?
A (1) Use transform and opacity (GPU accelerated); (2) avoid properties that trigger layout (width/height/top); (3) animate fewer elements at the same time.
Q How does Animate.css work with Vue?
A Use enter-active-class="animate__animated animate__fadeIn" to map Animate.css class names onto Vue's hooks.

📖 Summary


📝 Exercises

  1. Basic (difficulty ⭐) Build a simple fade-in/fade-out modal:

    • Clicking the button opens the modal
    • It fades in over 0.3 seconds
    • Clicking close fades it out
    • The backdrop fades in and out in sync
  2. Intermediate (difficulty ⭐⭐) Build a TransitionGroup list animation:

    • 5 todo items
    • New items fade in (0.5s)
    • Removed items fade out (0.5s + position: absolute)
    • Remaining items glide to their new position when reordered (.list-move)
  3. Challenge (difficulty ⭐⭐⭐) Build a complete "page transition" system:

    1. fade + slide on route changes
    2. 3 animations for list add/remove/reorder
    3. scale + fade combo for modals
    4. a complex timeline animation integrated with GSAP
    5. a side-by-side comparison of 5 third-party animation libraries
    6. performance optimization (GPU acceleration + will-change)
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏