Transitions and Animations
Vue ships built-in <Transition> and <TransitionGroup> components that automate the tedious part of animating elements as they enter or leave the DOM — Vue detects when a wrapped element is about to be inserted or removed (because of v-if, v-show, dynamic components, or route changes) and, instead of applying the change instantly, gives you a window of time to run a CSS transition, a CSS animation, or JavaScript hooks before completing the DOM operation. This means you write ordinary CSS classes and let Vue handle exactly when to add and remove them relative to the actual DOM mutation.
Cricket analogy: Like a stadium's automated roof system that detects rain starting (an element about to enter) and gives itself a timed window to smoothly close the panels with a motorized transition rather than slamming shut instantly, using the same simple hydraulic controls for every match.
The Six Transition CSS Classes
For a <Transition name="fade">, Vue applies up to six class combinations at the right moments: fade-enter-from/fade-enter-active/fade-enter-to for the entering phase, and fade-leave-from/fade-leave-active/fade-leave-to for the leaving phase. The -active classes are present for the whole duration and are where you typically declare the transition CSS property itself; the -from/-to classes describe the starting and ending style states that the browser interpolates between.
Cricket analogy: Like a floodlight dimming sequence with a defined 'ramping' phase (-active, where the dimmer's fade rate is set) bracketed by a starting brightness state (-from) and an ending brightness state (-to), so the lights smoothly ramp from full glare down to off rather than switching abruptly.
<script setup>
import { ref } from 'vue'
const showPanel = ref(false)
</script>
<template>
<button @click="showPanel = !showPanel">Toggle</button>
<Transition name="fade-slide">
<div v-if="showPanel" class="panel">Panel content</div>
</Transition>
</template>
<style scoped>
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: opacity 0.25s ease, transform 0.25s ease;
}
.fade-slide-enter-from,
.fade-slide-leave-to {
opacity: 0;
transform: translateY(-8px);
}
</style>Animating Lists With TransitionGroup
<Transition> only wraps a single element (or one element at a time when toggling between two), so animating a v-for list — items being added, removed, or reordered — requires <TransitionGroup> instead. Unlike <Transition>, <TransitionGroup> renders a real wrapper element by default (a <span> unless you set tag) and additionally applies a -move class using FLIP-technique CSS transforms so that when items shift position (e.g. after a sort or a removal shrinking the list), the remaining items animate smoothly to their new positions rather than jumping instantly.
Cricket analogy: Like a single player walking out to bat can get an individual entrance ceremony, but when the whole fielding team repositions after a bowling change, you need a coordinated team-wide reshuffle (TransitionGroup) with an actual huddle formation on the ground (wrapper element), where each fielder smoothly jogs to their new position (FLIP -move) rather than teleporting there.
React's Transition Group libraries (like react-transition-group or Framer Motion) require more manual wiring of enter/exit states; Vue's approach is closer to Angular's built-in animation triggers in that the framework itself detects DOM insertion/removal timing and exposes named CSS-class hooks automatically.
JavaScript Hooks for Complex Animations
For animations that CSS classes alone can't express — staggering multiple elements, integrating a JS animation library like GSAP, or coordinating with measurements of the DOM — <Transition> exposes JavaScript hooks (@before-enter, @enter, @after-enter, @leave, and their @after-leave counterparts) as component events. When using JS-only hooks without any CSS transition classes, you must call the done callback provided to @enter/@leave (or set :css="false") so Vue knows exactly when the animation has finished and it's safe to proceed.
Cricket analogy: Like a stadium's elaborate opening ceremony that needs a stage manager's manual 'go' cues for each fireworks burst (JS hooks) rather than a simple timed light dimmer, calling a 'sequence complete' signal (done callback) explicitly so the ground crew knows exactly when it's safe to start the toss.
A common pitfall is forgetting that <Transition> only animates a single root element being toggled — wrapping a v-for block directly in <Transition> (instead of <TransitionGroup>) will not animate individual list items correctly and often produces confusing behavior since Vue can't tell which specific item is entering or leaving.
<Transition>automatically applies enter/leave CSS classes around elements toggled by v-if, v-show, or dynamic components.- The six classes (enter-from/active/to, leave-from/active/to) let you declare transition timing in the -active class and start/end states in -from/-to.
<TransitionGroup>animates v-for lists, including a -move class for smoothly repositioning items using FLIP-style transforms.- JavaScript hooks (@enter, @leave, etc.) enable complex or library-driven animations, requiring an explicit done() call when :css="false".
- TransitionGroup renders a real wrapper element (default span) unlike Transition, which does not add extra DOM nodes.
- Using <Transition> around a v-for list instead of <TransitionGroup> will not animate individual items correctly.
Practice what you learned
1. Which CSS class holds the `transition` property duration/easing declaration for a Vue `<Transition name="fade">`?
2. Why is `<TransitionGroup>` needed instead of `<Transition>` for animating a v-for list?
3. What does the `-move` class added by TransitionGroup accomplish?
4. When using only JavaScript hooks (no CSS transition classes) for an enter animation, what must you do?
5. What DOM behavior distinguishes TransitionGroup from Transition by default?
Was this page helpful?
You May Also Like
Conditional Rendering: v-if and v-show
Explains Vue's two conditional rendering directives, how they differ at the DOM level, and how to choose the right one for performance and correctness.
List Rendering with v-for
Covers how v-for renders lists and objects in Vue templates, why the key attribute matters, and common performance and correctness pitfalls.
Computed Caching and Performance
Explains how Vue's computed properties memoize their results based on reactive dependencies, and how to use that caching behavior deliberately to avoid wasteful recalculation in components.
reactive() and ref()
A deep dive into Vue 3's two core reactivity primitives — ref() for any value type and reactive() for objects — and when to use each.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics