Chapter 39: Vue Animations
Vue Animations in Vue 3 (the modern 2026 way).
Animations in Vue are not just “nice to have” — they are one of the things that make your app feel professional, alive, and delightful instead of flat and jarring. When something appears, disappears, moves, or changes state, a tiny bit of smooth animation can make the difference between “okay app” and “wow, this feels premium”.
Vue gives you several layers of animation tools, from dead-simple CSS transitions to full-blown JavaScript-orchestrated animations. Today we’ll go through all the important ones step by step — with real, copy-paste-ready examples.
1. The Foundation: CSS Transitions + <Transition>
Vue’s built-in <Transition> component is the most common and most powerful way to animate enter/leave or state changes.
Basic Pattern – Fade In/Out
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<template> <div class="demo"> <button @click="show = !show"> {{ show ? 'Hide' : 'Show' }} Message </button> <!-- This is the magic wrapper --> <Transition name="fade"> <div v-if="show" class="message"> Hello from Hyderabad! 🌶️ </div> </Transition> </div> </template> <script setup> import { ref } from 'vue' const show = ref(false) </script> <style scoped> .message { margin-top: 1rem; padding: 1rem; background: #fef3c7; border-radius: 8px; font-size: 1.2rem; } /* Transition classes – Vue adds/removes them automatically */ .fade-enter-active, .fade-leave-active { transition: opacity 0.4s ease; } .fade-enter-from, .fade-leave-to { opacity: 0; } </style> |
How it works (very important)
Vue automatically adds/removes these 6 class names during enter/leave:
| Phase | Classes added | Typical CSS |
|---|---|---|
| Before enter | fade-enter-from | initial state (opacity: 0) |
| Enter starts | fade-enter-active | transition timing |
| Enter finishes | removes both, adds nothing | final state |
| Before leave | fade-leave-from (usually same as final) | — |
| Leave starts | fade-leave-active | transition timing |
| Leave finishes | fade-leave-to | end state (opacity: 0) |
Naming convention If you set name=”fade”, classes become:
- fade-enter-from / fade-enter-active / fade-enter-to
- fade-leave-from / fade-leave-active / fade-leave-to
You can also use no name (<Transition>) → classes are just v-enter-from, v-enter-active, etc.
2. Common Animation Patterns (You Will Use These Every Week)
A. Slide + Fade (very popular for modals, sidebars, dropdowns)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.slide-fade-enter-active { transition: all 0.4s ease; } .slide-fade-leave-active { transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1); } .slide-fade-enter-from, .slide-fade-leave-to { transform: translateX(20px); opacity: 0; } |
B. List / v-for Animations – <TransitionGroup>
This is magic for todo lists, cards, search results — items animate when added/removed/reordered.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<template> <div> <input v-model="newTask" @keyup.enter="addTask" placeholder="Add todo..." /> <TransitionGroup name="list" tag="ul"> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} <button @click="remove(todo.id)">×</button> </li> </TransitionGroup> </div> </template> <script setup> import { ref } from 'vue' const newTask = ref('') const todos = ref([ { id: 1, text: 'Learn TransitionGroup' }, { id: 2, text: 'Add animations' } ]) function addTask() { if (!newTask.value.trim()) return todos.value.push({ id: Date.now(), text: newTask.value.trim() }) newTask.value = '' } function remove(id) { todos.value = todos.value.filter(t => t.id !== id) } </script> <style scoped> .list-enter-active, .list-leave-active { transition: all 0.4s ease; } .list-enter-from, .list-leave-to { opacity: 0; transform: translateX(-30px); } .list-move { transition: transform 0.4s ease; } </style> |
Important:
- Use :key (must be unique & stable)
- <TransitionGroup> adds move class when items reorder
- tag=”ul” → renders as <ul> instead of <span>
3. JavaScript Hooks (When CSS is Not Enough)
You can hook into every phase with JS for complex animations (GSAP, Anime.js, Three.js, etc.)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<Transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" @enter-cancelled="enterCancelled" @before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave" @leave-cancelled="leaveCancelled" > <div v-if="show">Content</div> </Transition> <script setup> function beforeEnter(el) { el.style.opacity = '0' el.style.transform = 'scale(0.8)' } function enter(el, done) { // GSAP example gsap.to(el, { opacity: 1, scale: 1, duration: 0.6, ease: 'back.out(1.7)', onComplete: done }) } function afterEnter(el) { console.log('Animation finished') } </script> |
4. Quick Reference Table – Vue Animation Tools (2026)
| Tool | Best For | Syntax / Component | Learning Curve | Use Frequency |
|---|---|---|---|---|
| <Transition> | Single element enter/leave, state change | <Transition name=”fade”> | Low | ★★★★★ |
| <TransitionGroup> | Lists (v-for) – add/remove/reorder | <TransitionGroup name=”list”> | Medium | ★★★★☆ |
| CSS variables + classes | Theme changes, dark mode | :class, v-bind() in <style> | Low | ★★★★☆ |
| JavaScript hooks | Complex/3rd-party animations (GSAP, Anime.js) | @enter, @leave callbacks | High | ★★★☆☆ |
| <Transition> + <KeepAlive> | Cached tabs with animations | Wrap <router-view> or component | Medium | ★★★★☆ |
Pro Tips from Real 2026 Projects
-
Always test mobile — animations feel different on 60Hz vs 120Hz screens
-
Use cubic-bezier or ease-out curves → feels more natural than linear
-
Keep durations 0.2s–0.6s — too long = annoying, too short = invisible
-
Combine with Prefers-reduced-motion media query
CSS012345678910@media (prefers-reduced-motion: reduce) {.fade-enter-active, .fade-leave-active {transition: none;}} -
For lists → always use <TransitionGroup> + :key — otherwise animations break on reorder
-
Use FLIP technique libraries (GSAP Flip, vueuse/motion) for advanced move animations
Your Mini Practice Task
- Create a todo list with <TransitionGroup>
- Add fade + slide when adding/removing items
- Add a “Clear completed” button → animate removal of multiple items
- Bonus: wrap in <KeepAlive> + tabs so completed tab remembers scroll position
Any part confusing? Want full examples for:
- Modal with slide-up + backdrop fade?
- Animated router-view transitions?
- GSAP + Vue Transition hooks?
- Animate on data change (not just v-if)?
Just tell me — we’ll animate the next beautiful component together 🚀
Happy animating from Hyderabad! 💙
