Vue Transitions and Animations
The <Transition> Component
Vue's built-in <Transition> component applies enter/leave animations to a single element or component. It automatically adds CSS classes at the right moments, or you can use JavaScript hooks for full control.
| Class | When applied |
|---|---|
v-enter-from | Start state of enter — added before element is inserted |
v-enter-active | Active state of enter — applied during entire enter phase |
v-enter-to | End state of enter — added after element is inserted |
v-leave-from | Start state of leave |
v-leave-active | Active state of leave |
v-leave-to | End state of leave |
<template>
<div>
<button @click="show = !show">Toggle</button>
<!-- Basic fade transition -->
<Transition name="fade">
<p v-if="show">Hello, I fade in and out!</p>
</Transition>
<!-- Slide transition -->
<Transition name="slide">
<div v-if="show" class="panel">Sliding panel</div>
</Transition>
<!-- Mode: out-in (leave first, then enter) -->
<Transition name="fade" mode="out-in">
<component :is="currentView" :key="currentView" />
</Transition>
<!-- appear: animate on initial render -->
<Transition name="fade" appear>
<p>I animate when the page loads</p>
</Transition>
</div>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
const currentView = ref('HomeView')
</script>
<style>
/* Fade transition */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* Slide transition */
.slide-enter-active {
transition: all 0.3s ease-out;
}
.slide-leave-active {
transition: all 0.3s ease-in;
}
.slide-enter-from {
transform: translateX(-100%);
opacity: 0;
}
.slide-leave-to {
transform: translateX(100%);
opacity: 0;
}
/* Scale + fade */
.scale-enter-active,
.scale-leave-active {
transition: all 0.25s ease;
}
.scale-enter-from,
.scale-leave-to {
transform: scale(0.9);
opacity: 0;
}
</style>
<!-- TransitionGroup — animate lists -->
<template>
<div>
<input v-model="newItem" @keyup.enter="addItem" placeholder="Add item" />
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
<button @click="removeItem(item.id)">✕</button>
</li>
</TransitionGroup>
</div>
</template>
<script setup>
import { ref } from 'vue'
const newItem = ref('')
const items = ref([
{ id: 1, text: 'Learn Vue' },
{ id: 2, text: 'Build something' },
])
function addItem() {
if (!newItem.value.trim()) return
items.value.push({ id: Date.now(), text: newItem.value })
newItem.value = ''
}
function removeItem(id) {
items.value = items.value.filter(i => i.id !== id)
}
</script>
<style>
/* List item enter/leave */
.list-enter-active,
.list-leave-active {
transition: all 0.4s ease;
}
.list-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* Smooth repositioning of remaining items */
.list-move {
transition: transform 0.4s ease;
}
/* Prevent layout shift during leave */
.list-leave-active {
position: absolute;
}
</style>
<!-- JavaScript hooks — full control with GSAP or Web Animations API -->
<template>
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
:css="false"
>
<div v-if="show" class="box">Animated box</div>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
// :css="false" — disable CSS transitions, use JS only
function onBeforeEnter(el) {
el.style.opacity = '0'
el.style.transform = 'scale(0.8)'
}
function onEnter(el, done) {
// Use Web Animations API
el.animate([
{ opacity: 0, transform: 'scale(0.8)' },
{ opacity: 1, transform: 'scale(1)' }
], {
duration: 400,
easing: 'ease-out'
}).onfinish = done // call done() when animation completes
}
function onLeave(el, done) {
el.animate([
{ opacity: 1, transform: 'scale(1)' },
{ opacity: 0, transform: 'scale(0.8)' }
], {
duration: 300,
easing: 'ease-in'
}).onfinish = done
}
function onAfterEnter(el) { console.log('Enter complete') }
function onAfterLeave(el) { console.log('Leave complete') }
function onBeforeLeave(el) { /* ... */ }
function onEnterCancelled(el) { /* ... */ }
</script>
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.