Vue.js course Β· Module 13: Transitions & Animations

Transition Between Routes

3 min read
In this lesson5

At NOVA LAB, transitioning between station sections - from the command center to the laboratory, from the reactor to the hangar - should be smooth and natural. Vue Router supports transition animations between pages thanks to its integration with the <Transition> component.

RouterView + Transition

To animate router view changes, you use the slot API on <RouterView>. Vue Router provides the current component as a slot variable:

1<RouterView v-slot="{ Component }">
2  <Transition name="page" mode="out-in">
3    <component :is="Component" />
4  </Transition>
5</RouterView>

Here's how it works step by step:

  1. <RouterView v-slot="{ Component }"> - the router provides the current page component as Component
  2. <Transition name="page" mode="out-in"> - you wrap the dynamic component in a Transition
  3. <component :is="Component" /> - you render the dynamic page component

Three key rules:

  • Use v-slot="{ Component }" - you cannot directly wrap <RouterView> in <Transition> (this won't work in Vue 3)
  • Always add mode="out-in" - without it, the old and new pages will animate simultaneously, which looks chaotic
  • Don't add :key on <component> - Vue Router automatically reacts to route changes

The corresponding CSS classes:

1.page-enter-active,
2.page-leave-active {
3  transition: all 0.3s ease;
4}
5
6.page-enter-from {
7  opacity: 0;
8  transform: translateX(20px);
9}
10
11.page-leave-to {
12  opacity: 0;
13  transform: translateX(-20px);
14}

Per-Route Transitions

Each section of the NOVA LAB station can have a different animation type - the command center slides in from the left, the alerts panel pops up from below. Use the meta field in the route definition to assign an animation name:

1const routes = [
2  {
3    path: '/dashboard',
4    component: Dashboard,
5    meta: { transition: 'slide-left' }
6  },
7  {
8    path: '/settings',
9    component: Settings,
10    meta: { transition: 'slide-right' }
11  },
12  {
13    path: '/alerts',
14    component: Alerts,
15    meta: { transition: 'slide-up' }
16  }
17]

In the template, read the animation name from the route object:

1<RouterView v-slot="{ Component, route }">
2  <Transition :name="route.meta.transition || 'fade'" mode="out-in">
3    <component :is="Component" />
4  </Transition>
5</RouterView>

Note the || 'fade' - this is a fallback for routes without a defined animation. The v-slot gives you access to both Component (the current page component) and route (the route object with meta data).

Dynamic Animations Based on Navigation Direction

You can change the animation depending on whether the user is navigating "forward" or "backward":

1<script setup>
2import { ref } from 'vue'
3import { useRouter } from 'vue-router'
4
5const transitionName = ref('slide-left')
6const router = useRouter()
7
8router.beforeEach((to, from) => {
9  // Compare page depths to determine direction
10  const toDepth = to.path.split('/').length
11  const fromDepth = from.path.split('/').length
12
13  transitionName.value = toDepth < fromDepth ? 'slide-right' : 'slide-left'
14})
15</script>
16
17<template>
18  <RouterView v-slot="{ Component }">
19    <Transition :name="transitionName" mode="out-in">
20      <component :is="Component" />
21    </Transition>
22  </RouterView>
23</template>

With KeepAlive

You can combine Transition with KeepAlive to cache page views. This is useful when the user frequently switches between the same station sections and you don't want to lose state (e.g., scroll position, entered data):

1<RouterView v-slot="{ Component }">
2  <Transition name="fade" mode="out-in">
3    <KeepAlive :max="5">
4      <component :is="Component" />
5    </KeepAlive>
6  </Transition>
7</RouterView>

The order matters - <Transition> on the outside, <KeepAlive> on the inside. The :max="5" attribute limits the number of cached components to avoid excessive memory usage.

Practical Notes

  • Page transition animations should be short (200-400ms) - the user wants to see the content quickly
  • Avoid heavy animations (e.g., 3D transforms) on mobile devices
  • Test animations on slower devices - an effect that looks great on a fast computer may lag on a tablet
  • Prefer opacity and transform - these properties are GPU-accelerated
Code for this lesson: App.vue
1<script setup>
2import { ref } from 'vue'
3
4// Router simulation with components
5const currentRoute = ref('home')
6const routes = {
7  home: { name: 'Dashboard', icon: '' },
8  sensors: { name: 'Sensors', icon: '' },
9  crew: { name: 'Crew', icon: '' }
10}
11</script>
12
13<template>
14  <div class="nova-lab">
15    <h1>Route Transitions</h1>
16    <p style="color: #888;">Transition simulation with RouterView</p>
17
18    <nav class="nav">
19      <button
20        v-for="(route, key) in routes"
21        :key="key"
22        @click="currentRoute = key"
23        :class="{ active: currentRoute === key }"
24        class="nav-link"
25      >
26        {{ route.icon }} {{ route.name }}
27      </button>
28    </nav>
29
30    <!-- Simulation: RouterView v-slot + Transition -->
31    <Transition name="page" mode="out-in">
32      <div :key="currentRoute" class="page">
33        <h2>{{ routes[currentRoute].icon }} {{ routes[currentRoute].name }}</h2>
34        <p>Page content: {{ currentRoute }}</p>
35      </div>
36    </Transition>
37  </div>
38</template>
39
40<style scoped>
41.nova-lab {
42  background: #0a0e27;
43  color: #00ff88;
44  min-height: 100vh;
45  padding: 20px;
46  font-family: monospace;
47}
48h1 { color: #00ff88; }
49.nav {
50  display: flex;
51  gap: 8px;
52  margin: 15px 0;
53  border-bottom: 2px solid #1a1e3a;
54  padding-bottom: 10px;
55}
56.nav-link {
57  background: transparent;
58  color: #00b4d8;
59  border: 1px solid transparent;
60  padding: 8px 16px;
61  border-radius: 6px 6px 0 0;
62  cursor: pointer;
63}
64.nav-link.active {
65  background: rgba(0, 180, 216, 0.15);
66  border-color: #00b4d8;
67  border-bottom-color: transparent;
68}
69.page {
70  background: rgba(0, 255, 136, 0.05);
71  border: 2px solid #00ff88;
72  padding: 30px;
73  border-radius: 0 12px 12px 12px;
74  min-height: 150px;
75}
76
77/* Page transition */
78.page-enter-active,
79.page-leave-active {
80  transition: all 0.3s ease;
81}
82.page-enter-from {
83  opacity: 0;
84  transform: translateY(20px);
85}
86.page-leave-to {
87  opacity: 0;
88  transform: translateY(-20px);
89}
90</style>

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. How do you add a transition animation between pages in Vue Router?

  2. 2. How do you define a per-route animation name in Vue Router?

Hands-on tasks in the game

  • Vertical ordering

    Arrange the component nesting for routing animation:

  • Code editor

    Wrap RouterView v-slot in <Transition> with mode='out-in'.

  • Horizontal ordering

    Arrange the RouterView syntax with v-slot:

  • Click in order

    Arrange the code for a dynamic per-route transition name:

Useful articles