Vue Animations
Animate elements and lists with Vue transitions.
Overview
Vue's built-in Transition and TransitionGroup components coordinate visual effects when elements enter, leave, or move. Vue applies classes at the correct moments while CSS defines the appearance, duration, and motion.
Key concepts
- Transition handles one element or component
- TransitionGroup handles keyed lists
- CSS classes define enter and leave phases
- Motion should be purposeful and accessible
What are animations?
Animations are visual changes that help elements appear, disappear, or move smoothly. A menu can slide into view, a notification can fade away, and a cart item can animate before it is removed.
Useful motion explains cause and effect. It reassures users that an action happened, preserves spatial context, and draws attention to meaningful changes. It should support the interface rather than compete with it.
The Transition component
Transition wraps one conditional element or component. When v-if or v-show changes, Vue detects the entering or leaving element and applies transition classes automatically.
<script setup>
import { ref } from "vue";
const show = ref(true);
</script>
<template>
<button @click="show = !show">Toggle message</button>
<Transition name="fade">
<p v-if="show">Welcome to Vue.js!</p>
</Transition>
</template>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 250ms ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>How transition classes work
The name prop becomes the CSS class prefix. With name="fade", Vue applies fade-enter-from, fade-enter-active, and fade-enter-to while entering, then fade-leave-from, fade-leave-active, and fade-leave-to while leaving.
- enter-from: the starting state before insertion
- enter-active: timing and easing for the full enter phase
- enter-to: the ending state of the enter phase
- leave-from: the starting state of the leave phase
- leave-active: timing and easing for the full leave phase
- leave-to: the ending state before removal
Slide and fade example
Combining opacity with transform creates smooth motion without animating layout-heavy properties.
.slide-enter-active,
.slide-leave-active {
transition: opacity 250ms ease, transform 250ms ease;
}
.slide-enter-from,
.slide-leave-to {
opacity: 0;
transform: translateY(12px);
}Using v-if and v-show
Transition works with both directives. v-if inserts and removes the element, while v-show keeps the element in the DOM and toggles its display style. v-show can be useful for content that changes visibility frequently.
Transitioning between elements
Use unique keys when Vue must recognize two states as different elements. The out-in mode finishes the old element's leave transition before the new one enters, avoiding overlap.
<Transition name="fade" mode="out-in">
<p :key="status">
{{ status === "saved" ? "Saved successfully" : "Saving..." }}
</p>
</Transition>Animating on initial render
Add the appear prop when an element should run its enter effect the first time the component renders.
<Transition name="fade" appear>
<h1>Welcome back</h1>
</Transition>Animating lists with TransitionGroup
TransitionGroup animates additions, removals, and position changes in a list. Every child needs a stable, unique key so Vue can track its identity.
<script setup>
import { ref } from "vue";
const nextId = ref(3);
const items = ref([
{ id: 1, name: "Apples" },
{ id: 2, name: "Bread" }
]);
function addItem() {
const id = nextId.value++;
items.value.push({ id, name: "Item " + id });
}
function removeItem(id) {
items.value = items.value.filter((item) => item.id !== id);
}
</script>
<template>
<button @click="addItem">Add item</button>
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.name }}
<button @click="removeItem(item.id)">Remove</button>
</li>
</TransitionGroup>
</template>
<style scoped>
.list-enter-active,
.list-leave-active,
.list-move {
transition: opacity 250ms ease, transform 250ms ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(20px);
}
.list-leave-active {
position: absolute;
}
</style>CSS animations
Vue also detects CSS keyframe animations. Apply the animation to the active class when an effect needs multiple stages rather than a simple start-to-end transition.
.bounce-enter-active {
animation: bounce-in 300ms;
}
.bounce-leave-active {
animation: bounce-in 300ms reverse;
}
@keyframes bounce-in {
0% { transform: scale(0.8); opacity: 0; }
60% { transform: scale(1.05); }
100% { transform: scale(1); opacity: 1; }
}Performance
Prefer animating transform and opacity because browsers can usually render them without recalculating page layout. Be cautious with width, height, top, and left animations, especially across many elements.
Respecting reduced motion
Some users reduce motion at the operating-system level because movement can cause discomfort. Honor that preference by removing or greatly shortening nonessential effects.
@media (prefers-reduced-motion: reduce) {
.fade-enter-active,
.fade-leave-active,
.slide-enter-active,
.slide-leave-active {
transition-duration: 1ms;
}
}Best practices
- Use motion to explain changes or provide feedback
- Keep most interface transitions brief
- Use consistent durations and easing
- Prefer transform and opacity
- Keep controls usable while effects run
- Respect prefers-reduced-motion
- Test on lower-powered devices
- Use stable keys for TransitionGroup items
Common beginner mistakes
- Forgetting to wrap conditional content in Transition
- Using a CSS prefix that does not match the name prop
- Trying to place multiple uncoordinated children inside Transition
- Forgetting stable keys in TransitionGroup
- Making transitions too slow
- Animating every element without purpose
- Using layout-heavy properties for large effects
- Ignoring reduced-motion preferences
Key takeaways
- Transition animates one entering or leaving element or component
- TransitionGroup handles keyed list changes
- Vue controls transition class timing while CSS defines the effect
- The name prop determines the class prefix
- mode can coordinate transitions between elements
- Transform and opacity usually provide the smoothest performance
- Accessible animations respect reduced-motion preferences
- Subtle, purposeful motion improves feedback and understanding