Transitions | Vue Router - Smooth Page Animations ✨
Seamless navigation experiences - Add beautiful animations between routes!
🎯 Overview
Vue Router integrates seamlessly with Vue's transition system, allowing you to create smooth animations between route changes. This enhances user experience by providing visual feedback during navigation and creating polished, professional applications.
📋 Key Benefits
- Visual Continuity: Smooth transitions between pages
- User Feedback: Visual indicators during navigation
- Professional Polish: Enhanced user experience
- Customizable Effects: Wide range of animation possibilities
💡 When to Use Transitions
🎨 Enhanced User Experience
Page Transitions
- Fade between routes
- Slide animations for forward/back navigation
- Custom enter/leave effects
Loading States
- Show loading indicators during route changes
- Progressive content revelation
- Skeleton screens during loading
🔧 Technical Integration
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition name="fade" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</template>🚀 Basic Transition Setup
Simple Fade Transition
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition name="fade" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>Slide Transition
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition name="slide" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</template>
<style>
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter-from {
opacity: 0;
transform: translateX(30px);
}
.slide-leave-to {
opacity: 0;
transform: translateX(-30px);
}
</style>🔧 Advanced Techniques
Example 1: Direction-Aware Transitions
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition :name="transitionName" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const transitionName = ref('slide-left')
// Track navigation direction
router.beforeEach((to, from) => {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
transitionName.value = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})
</script>
<style>
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 0.3s ease;
}
.slide-left-enter-from {
opacity: 0;
transform: translateX(30px);
}
.slide-left-leave-to {
opacity: 0;
transform: translateX(-30px);
}
.slide-right-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.slide-right-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>Example 2: Route-Specific Transitions
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition :name="getTransitionName(route)" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</template>
<script setup>
function getTransitionName(route) {
return route.meta.transition || 'fade'
}
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.zoom-enter-active,
.zoom-leave-active {
transition: all 0.3s ease;
}
.zoom-enter-from {
opacity: 0;
transform: scale(0.9);
}
.zoom-leave-to {
opacity: 0;
transform: scale(1.1);
}
</style>🛠️ Practical Examples
Example 1: E-commerce Site Transitions
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition :name="getEcommerceTransition(route)" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</template>
<script setup>
function getEcommerceTransition(route) {
// Different transitions for different sections
if (route.path === '/') return 'fade'
if (route.path === '/products') return 'slide-left'
if (route.path.startsWith('/products/')) return 'zoom'
if (route.path === '/cart') return 'slide-up'
return 'fade'
}
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.slide-left-enter-active,
.slide-left-leave-active {
transition: all 0.3s ease;
}
.slide-left-enter-from {
opacity: 0;
transform: translateX(30px);
}
.slide-left-leave-to {
opacity: 0;
transform: translateX(-30px);
}
.zoom-enter-active,
.zoom-leave-active {
transition: all 0.3s ease;
}
.zoom-enter-from {
opacity: 0;
transform: scale(0.9);
}
.zoom-leave-to {
opacity: 0;
transform: scale(1.1);
}
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.3s ease;
}
.slide-up-enter-from {
opacity: 0;
transform: translateY(30px);
}
.slide-up-leave-to {
opacity: 0;
transform: translateY(-30px);
}
</style>Example 2: Loading States with Transitions
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition name="fade" mode="out-in">
<div v-if="!isLoading" key="content">
<component :is="Component" />
</div>
<div v-else key="loading" class="loading-state">
<LoadingSpinner />
<p>Loading {{ route.meta.title }}...</p>
</div>
</Transition>
</RouterView>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const isLoading = ref(false)
// Show loading during navigation
router.beforeEach(() => {
isLoading.value = true
})
router.afterEach(() => {
isLoading.value = false
})
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.loading-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
text-align: center;
}
</style>🔍 Transition Modes
Understanding Transition Modes
vue
<!-- Mode: "out-in" - Current leaves, then new enters -->
<Transition name="fade" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
<!-- Mode: "in-out" - New enters, then current leaves -->
<Transition name="fade" mode="in-out">
<component :is="Component" :key="route.path" />
</Transition>
<!-- Mode: (default) - Both animate simultaneously -->
<Transition name="fade">
<component :is="Component" :key="route.path" />
</Transition>JavaScript Hooks
vue
<template>
<RouterView v-slot="{ Component, route }">
<Transition
:css="false"
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
>
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</template>
<script setup>
function onBeforeEnter(el) {
console.log('Before enter:', el)
}
function onEnter(el, done) {
// Use GSAP or other animation libraries
console.log('Entering:', el)
setTimeout(done, 300) // Call done when animation completes
}
function onAfterEnter(el) {
console.log('After enter:', el)
}
function onBeforeLeave(el) {
console.log('Before leave:', el)
}
function onLeave(el, done) {
console.log('Leaving:', el)
setTimeout(done, 300)
}
function onAfterLeave(el) {
console.log('After leave:', el)
}
</script>🚀 Best Practices
✅ Do
- Use Appropriate Durations: 200-500ms for smooth transitions
- Consider Performance: Avoid complex animations on low-end devices
- Respect User Preferences: Check
prefers-reduced-motion - Test Thoroughly: Ensure transitions work across browsers
❌ Don't
- Overanimate: Too many effects can be distracting
- Ignore Accessibility: Ensure animations don't hinder usability
- Forget Mobile: Test on touch devices
- Skip Fallbacks: Provide no-animation fallbacks
📚 Related Features
- CSS Transitions: Leverage CSS for smooth animations
- JavaScript Hooks: Custom animation logic
- Transition Groups: Animate multiple elements
- Animation Libraries: GSAP, Anime.js, etc.
💡 Pro Tip
Combine Vue Router transitions with the prefers-reduced-motion media query to respect user accessibility preferences while still providing enhanced experiences for those who want them!
Ready to create stunning navigation? Start implementing smooth transitions to elevate your Vue Router applications! ✨