Skip to content

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
  • 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! ✨

🚀 Vue Router - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择