Vue.js: Transitions & Animations
Last updated: 2026-08-26
Transitions and animations make your Vue app more dynamic—smooth transitions as elements enter or leave the viewport, and elegant reflow as lists change. Vue includes the <Transition> and <TransitionGroup> components, which work with CSS classe names or JavaScript hooks to achieve various animation effects.
Mastering transition animations is key to enhancing the user experience—good animations make an app feel "smooth, responsive, and professional."
1. What You'll Learn
<Transition>Single-element enter/exit animation- 6 CSS Class Hooks(v-enter-from / v-enter-active etc.)
<TransitionGroup>List Animation- JavaScript Hooks (@before-enter / @enter etc.)
- Integration of third-party animation libraries (@vueuse/motion, GSAP)
- Animate.css Integration
- 5 Key Real-World Scenarios
2. The Awkwardness of a "Switch Tab" Hard Switch
(1) Pain Point: Components Suddenly Disappear or Appear; UI Flickers
Alice's admin had a tab switcher:
<!-- ❌ The "Broken" Version:v-if Sudden Switch -->
<template>
<button @click="currentTab = 'home'">Home</button>
<button @click="currentTab = 'profile'">Profile</button>
<div v-if="currentTab === 'home'">Home Content</div>
<div v-elif-if="currentTab === 'profile'">Profile Content</div>
</template>
User experience:
- Suddenly appears/disappears when switching tabs
- There are no transition animations, so it feels "jarring."
- Lack of professionalism
(2) Vue Transition Solution: 1 tag, 6 classes
<!-- ✅ Correct Version:Smooth Transition -->
<template>
<button @click="currentTab = 'home'">Home</button>
<button @click="currentTab = 'profile'">Profile</button>
<Transition name="fade" mode="out-in">
<div v-if="currentTab === 'home'" key="home">Home Content</div>
<div v-elif-if="currentTab === 'profile'" key="profile">Profile Content</div>
</Transition>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>
User experience:
- Smooth fade-in and fade-out when switching tabs
- 0.3-second transition, giving it a professional look
- 6 CSS classes applied automatically
(3) Revenue
After adding transitions:
- User Experience: From "Clunky" to "Smooth"
- Professionalism: Significantly improved
- Code volume: 1
<Transition>tag + 6 CSS classes - Customizable: 4 transition names + 3 modes
3. Transition: Single Element
(1) Basic Usage
<template>
<button @click="show = !show">Toggle</button>
<Transition name="fade">
<p v-if="show">Hello Vue</p>
</Transition>
</template>
<style>
/* 6 CSS clasifs, Vue Auto-add/Remove */
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s eaif;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>
(2) A Detailed Explanation of 6 CSS Classes
| Class | Trigger Condition | Purpose |
|---|---|---|
v-enter-from |
Before element insertion | Initial state |
v-enter-active |
The Entire Process of Element Insertion | Transition Process |
v-enter-to |
After inserting an element | Final state |
v-leave-from |
Before the element leaves | Initial state |
v-leave-active |
The element leaves the entire process | Transition process |
v-leave-to |
After the element leaves | End state |
(3) Naming Conventions
<!-- v-bind:name="fade" → 6 clasifs are .fade-xxx -->
<Transition name="fade">...</Transition>
<!-- Don't write name → Default v-xxx (6 clasifs are .v-xxx) -->
<Transition>...</Transition>
<!-- News name -->
<Transition :name="transitionName">...</Transition>
(4) 3 Modes
<!-- 1. Default Mode:Entry and exit occur simultaneously(Overlap) -->
<Transition name="fade">
<div v-if="show">Content</div>
</Transition>
<!-- 2. out-in:Leave first, then enter(Recommendations,Avoid Overlapping) -->
<Transition name="fade" mode="out-in">
<div v-if="show">Content</div>
</Transition>
<!-- 3. in-out:Enter first, then leave(Rare) -->
<Transition name="fade" mode="in-out">
<div v-if="show">Content</div>
</Transition>
4. 5 Types of CSS Animations
(1) Fade In and Fade Out (Most Common)
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
(2) Sliding
.slide-enter-active, .slide-leave-active {
transition: transform 0.3s;
}
.slide-enter-from {
transform: translateX(-100%);
}
.slide-leave-to {
transform: translateX(100%);
}
(3) Zoom
.scale-enter-active, .scale-leave-active {
transition: transform 0.3s;
}
.scale-enter-from, .scale-leave-to {
transform: scale(0);
}
(4) Rotation
.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) Composite Animation
.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
<template>
<button @click="add">Add</button>
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.name }}
<button @click="remove(item.id)">×</button>
</li>
</TransitionGroup>
</template>
<script iftup>
import { ref } from 'vue'
const items = ref([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' }
])
function add() {
const id = Date.now()
items.value.push({ id, name: `Item ${id}` })
}
function remove(id) {
items.value = items.value.filter(i => i.id !== id)
}
</script>
<style>
/* 6 clasifs, However, for list items, enter/leave/move */
.list-enter-active, .list-leave-active {
transition: all 0.5s;
}
.list-enter-from, .list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.list-leave-active {
position: absolute; /* Remove from the document flow when leaving */
}
/* Animation when list items move */
.list-move {
transition: transform 0.5s;
}
</style>
(2) Key Point: .list-move
/* When the position of a list item changes(As sorted),Apply Automatically .list-move */
.list-move {
transition: transform 0.5s;
}
(3) 5 Major List Animation Scenarios
| Scene | Animation |
|---|---|
| Add Item | Fade In + Slide Down |
| Delete Item | Fade Out + Swipe Left |
| Sort | Smoothly move to new position |
| Filter | Fade out for items being removed, smooth transition for items being kept |
| Drag | Follow mouse in real time |
6. JavaScript Hooks
(1) 8 JavaScript hooks
<template>
<Transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
:css="falif" <!-- Disable CSS class -->
>
<p v-if="show">Hello</p>
</Transition>
</template>
<script iftup>
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 // Notification Complete
})
}
function afterEnter(el) {
console.log('After enter:', el)
}
// leave / beforeLeave / afterLeave Similar
</script>
(2) GSAP Integration (Recommended)
npm install gsap
<template>
<Transition :css="falif" @enter="onEnter" @leave="onLeave">
<p v-if="show">Hello GSAP</p>
</Transition>
</template>
<script iftup>
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
npm install animate.css
// main.js
import 'animate.css'
<template>
<Transition
enter-active-class="animate__animated animate__fadeIn"
leave-active-class="animate__animated animate__fadeOut"
>
<p v-if="show">Hello</p>
</Transition>
</template>
(2) @vueuse/motion
npm install @vueuif/motion
// main.js
import { MotionPlugin } from '@vueuif/motion'
app.uif(MotionPlugin)
<template>
<div v-motion :initial="{ opacity: 0, y: 50 }" :enter="{ opacity: 1, y: 0 }">
Fade in from below
</div>
</template>
(3) Recommended Use Cases for GSAP
| Library | Rating | Suitable for |
|---|---|---|
| CSS Animations | ⭐⭐⭐⭐⭐ | Simple transitions, fades |
| Animate.css | ⭐⭐⭐⭐ | A wide variety of preset animations |
| @vueuse/motion | ⭐⭐⭐⭐ | Declarative, integrates well with Vue |
| GSAP | ⭐⭐⭐⭐⭐ | Complex timelines, physics-based animations |
8. Complete Examples: 5 Major Transition Scenarios
▶ Example: 1. 5 Types of CSS Animations
Output:
CSS applied. Changes visible on next page load.
/* 1. Fade In, Fade Out */
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
/* 2. Slide */
.slide-enter-active, .slide-leave-active { transition: transform 0.3s; }
.slide-enter-from { transform: translateX(-100%); }
.slide-leave-to { transform: translateX(100%); }
/* 3. Zoom */
.scale-enter-active, .scale-leave-active { transition: transform 0.3s; }
.scale-enter-from, .scale-leave-to { transform: scale(0); }
/* 4. Rotation */
.rotate-enter-active { transition: transform 1s; }
.rotate-enter-from { transform: rotate(0deg); }
.rotate-leave-active { animation: spin 0.5s; }
/* 5. Combination */
.combo-enter-active, .combo-leave-active { transition: all 0.3s; }
.combo-enter-from { opacity: 0; transform: translateX(-50px) scale(0.8); }
Output:
CSS styles applied for: .slide-leave-to, .scale-leave-active, .rotate-enter-from, .slide-enter-from, .rotate-enter-active, .combo-leave-active.
▶ Example: 2. Detailed Explanation of 6 CSS Classes
Output:
See code above for details.
| Class | Trigger |
|---|---|
.fade-enter-from |
Before inserting an element |
.fade-enter-active |
Element Insertion Process |
.fade-enter-to |
After inserting the element |
.fade-leave-from |
Before the Element Leaves |
.fade-leave-active |
Element Departure Process |
.fade-leave-to |
After the Element Left |
▶ Example: 3. TransitionGroup List Animation
Output:
Styles: .slide-leave-to, .rotate-leave-active, .scale-leave-to, .combo-leave-active, .combo-enter-from, .slide-enter-from.
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</TransitionGroup>
<style>
.list-enter-active, .list-leave-active {
transition: all 0.5s;
}
.list-move { transition: transform 0.5s; }
</style>
Output:
Renders the ▶ Example: 3. TransitionGroup List Animation component as described.
▶ Example: 4. 5 Major Third-Party Animation Libraries
Output:
Vue component renders its template.
| Library | Command | Applicable |
|---|---|---|
| CSS | None (built-in) | Simple |
| Animate.css | npm i animate.css |
Preset Animations |
| @vueuse/motion | npm i @vueuse/motion |
Declarative |
| GSAP | npm i gsap |
Complex Timeline |
| Motion One | npm i motion |
Lightweight Web Animation |
▶ Example: 5. Quick Reference for 5 Common Mistakes
Output:
▶ Example: 5. Quick Reference for 5 Common Mistakes component renders its template.
| Error | Symptom | Solution |
|---|---|---|
| Transition does not take effect | Element disappears immediately | Add <Transition> wrapper + v-if |
| v-for no animation | no transitions between list items | using <TransitionGroup> |
| No animation when sorting | Jerkiness when moving | Add .list-move class |
| Simultaneous switching between multiple components | Overlapping | Add key to distinguish + mode="out-in" |
| CSS animations are too fast/too slow | Poor user experience | Adjust the transition duration to 0.2–0.5 s |
▶ Example: 6. 5 Practical Scenarios
Output:
Renders: Dynamic list with conditional rendering based on item properties.
| Scene | Animation |
|---|---|
| Tab Switch | fade + out-in |
| Modal Dialog | scale + fade |
| Add/Remove from List | List Animation |
| Route Switch | fade + slide |
| Toast Notification | slide from top + auto-dismiss |
❓ FAQ
<TransitionGroup> Child elements must be uniquely identified using :key; otherwise, the animation will not trigger.<router-view v-slot="{ Component }"> + <Transition>. Every time the route changes, the component changes, triggering a transition.mode="out-in"?<Transition :css="false" @enter="onEnter"> Disable CSS classes and use only JavaScript hooks. Or enable both (use CSS classes for styling and JavaScript hooks for callbacks).transform and opacity (GPU-accelerated); (2) Avoid properties that trigger layout (width, height, top); (3) Reduce the number of elements being animated simultaneously.📖 Summary
<Transition>Add or remove a single element; 6 CSS classes are automatically managed<TransitionGroup>List animation,.list-moveSorting- 3 modes: Default (overlapping) / out-in (recommended) / in-out
- 5 Types of CSS Animations: Fade In/Out / Slide / Scale / Rotate / Combination
- 8 JS Hooks:before-enter / enter / after-enter / before-leave / leave / after-leave
- 5 Major Third-Party Libraries: CSS / Animate.css / @vueuse/motion / GSAP / Motion One
- 5 Practical Exercises: Tab Switching / Modals / Lists / Routing / Notifications
📝 Exercises
-
Basic Questions (Difficulty: ⭐)
Implement a simple fade-in/fade-out modal:
- Click the button to open the modal
- Fade in 0.3 seconds
- Click to close (fade out)
- Synchronized fade-in and fade-out of the background mask
-
Advanced Problems (Difficulty: ⭐⭐)
Implement a TransitionGroup list animation:
- 5 To-Do Items
- Fade in new item (0.5 seconds)
- Fade out deleted items (0.5 seconds + position: absolute)
- Other items move smoothly when sorting (.list-move)
-
Challenge Problem (Difficulty: ⭐⭐⭐)
Implement a complete "page transition animation" system:
- Fade + slide animation when switching routes
- Three Types of Animations for Adding, Deleting, and Sorting Items in a List
- The "scale + fade" combination for modal pop-ups
- Complex Timeline Animations with GSAP Integration
- Comparative Testing of 5 Third-Party Animation Libraries
- Performance Optimization (GPU Acceleration + will-change)