Vue.js course Β· Module 13: Transitions & Animations
Named Transitions and Transition Modes
In this lesson4
At NOVA LAB we have many panels - the reactor diagnostics panel slides in from the side, the alerts panel appears with a fade effect, and the navigation panel drops from above. Vue lets you name Transitions, which changes the CSS class prefixes and allows you to define many different animations in a single application.
In the previous lesson every animation used classes with the v- prefix. That works as long as there is only one animation. Once you add a second one, the rules for v-enter-from and v-leave-to start applying to both panels at once, so instead of sliding in from the side the reactor starts fading like the alert. A second problem shows up when you swap panels: the old one is still leaving while the new one is already entering, so for a moment both take up space and the layout jumps. In this lesson you will remove both faults of the Holographic Chamber with two attributes.
Named Transitions
By default, <Transition> uses the v- prefix for CSS classes. The name attribute changes this prefix to a chosen name:
1<Transition name="slide">
2 <div v-if="visible">...</div>
3</Transition>Now the classes are: slide-enter-from, slide-enter-active, slide-enter-to, slide-leave-from, slide-leave-active, slide-leave-to. The name itself adds no effect at all - it only changes the class names, and you still describe what the animation looks like in CSS:
1.slide-enter-active,
2.slide-leave-active {
3 transition: all 0.3s ease;
4}
5.slide-enter-from {
6 opacity: 0;
7 transform: translateX(30px);
8}
9.slide-leave-to {
10 opacity: 0;
11 transform: translateX(-30px);
12}The classes ending in -active set how long the transition lasts and with which curve, while enter-from and leave-to say where the element flies in from and where it flies off to. This panel comes in from the right and leaves to the left.
Thanks to named transitions, you can have many types of animations at the same time:
1<template>
2 <!-- Reactor panel - slides in from the right -->
3 <Transition name="slide-right">
4 <div v-if="showReactor">Reactor Panel</div>
5 </Transition>
6
7 <!-- Alert - appears with a fade effect -->
8 <Transition name="fade">
9 <div v-if="showAlert">Alert!</div>
10 </Transition>
11
12 <!-- Menu - drops from above -->
13 <Transition name="slide-down">
14 <nav v-if="showMenu">Navigation</nav>
15 </Transition>
16</template>Each <Transition> looks only for classes with its own prefix, so the styles of the three panels do not get in each other's way:
1/* Fade */
2.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
3.fade-enter-from, .fade-leave-to { opacity: 0; }
4
5/* Slide from right */
6.slide-right-enter-active, .slide-right-leave-active { transition: all 0.4s ease; }
7.slide-right-enter-from { transform: translateX(100%); opacity: 0; }
8.slide-right-leave-to { transform: translateX(100%); opacity: 0; }
9
10/* Slide from top */
11.slide-down-enter-active, .slide-down-leave-active { transition: all 0.3s ease; }
12.slide-down-enter-from { transform: translateY(-100%); opacity: 0; }
13.slide-down-leave-to { transform: translateY(-100%); opacity: 0; }Notice that the template differs only in the value of the name attribute - the whole difference between sliding, fading and dropping lives in the stylesheet.
Transition Modes (mode)
When you switch between two elements inside a single <Transition>, by default both animate simultaneously - the old one leaves while the new one enters at the same time. This often looks chaotic. The mode attribute controls the order:
mode="out-in"- the old element leaves and only after its animation finishes does the new one enter. This is the most commonly used mode - it prevents layout "jumping."mode="in-out"- the new element enters, and the old one leaves only afterward. Rarely used, but useful for overlay effects.- No mode - both elements animate simultaneously. Can cause layout issues.
This is what a mission control view switcher with the out-in mode looks like:
1<template>
2 <div class="view-switcher">
3 <button @click="currentView = currentView === 'status' ? 'alerts' : 'status'">
4 Switch View
5 </button>
6
7 <Transition name="fade" mode="out-in">
8 <div v-if="currentView === 'status'" key="status" class="panel">
9 System Status: All nominal
10 </div>
11 <div v-else key="alerts" class="panel">
12 Active Alerts: 3
13 </div>
14 </Transition>
15 </div>
16</template>
17
18<script setup>
19import { ref } from 'vue'
20
21const currentView = ref('status')
22</script>After a click the status panel first disappears completely, and only then does the alerts panel appear, so at no point are there two panels on the screen at once.
Important: In Vue 3, v-if and v-else branches get unique keys automatically, so switching between two <div> elements works even without a key attribute - an explicit key, as in the example, does no harm and describes the intent well. A key becomes essential when the same element stays in place and only its content changes, for example <span :key="count">{{ count }}</span>. Without :key, Vue would only replace the text inside the existing element and the transition would not run at all.
Switching Components
mode="out-in" works great with dynamic components. The <component :is> element renders the component stored in a variable, and shallowRef keeps it without deep reactivity, because only swapping the whole component matters:
1<template>
2 <Transition name="slide" mode="out-in">
3 <component :is="currentTab" />
4 </Transition>
5</template>
6
7<script setup>
8import { shallowRef } from 'vue'
9import StatusPanel from './StatusPanel.vue'
10import AlertsPanel from './AlertsPanel.vue'
11
12const currentTab = shallowRef(StatusPanel)
13</script>With every change of currentTab, Vue first plays the leave animation of the old panel and only then the enter animation of the new one. StatusPanel and AlertsPanel are two different component definitions, so the animation runs without an extra key.
Practical Tip
For simple switches between views, mode="out-in" with a fade animation is the most elegant solution. Avoid mode="in-out" unless you have a specific reason (e.g., an overlapping slide effect). No mode works mainly when both elements have position: absolute and do not affect the layout.
My advice: name transitions after the movement, not after the place they are used in - slide-right and fade will come in handy in ten panels, while reactor-animation only in one. The same name and mode pair will return in the lesson on transitions between routes, where you will wrap the whole <RouterView> with it.
Remember: name gives an animation its own set of classes, and mode sets the queue - first one panel disappears, and only then does the other appear.
Code for this lesson: App.vue
1<script setup>
2import { ref } from 'vue'
3
4const currentView = ref('dashboard')
5const views = ['dashboard', 'sensors', 'crew']
6</script>
7
8<template>
9 <div class="nova-lab">
10 <h1>Named Transitions & Modes</h1>
11
12 <div class="nav">
13 <button
14 v-for="view in views"
15 :key="view"
16 @click="currentView = view"
17 :class="{ active: currentView === view }"
18 class="nav-btn"
19 >
20 {{ view }}
21 </button>
22 </div>
23
24 <!-- Transition z named classes i mode out-in -->
25 <Transition name="slide" mode="out-in">
26 <div :key="currentView" class="panel">
27 <h2>{{ currentView.toUpperCase() }}</h2>
28 <p v-if="currentView === 'dashboard'">NOVA LAB station main panel</p>
29 <p v-else-if="currentView === 'sensors'">Atmospheric sensor readings</p>
30 <p v-else>Crew member list</p>
31 </div>
32 </Transition>
33 </div>
34</template>
35
36<style scoped>
37.nova-lab {
38 background: #0a0e27;
39 color: #00ff88;
40 min-height: 100vh;
41 padding: 20px;
42 font-family: monospace;
43}
44h1 { color: #00ff88; }
45
46.nav { display: flex; gap: 10px; margin: 15px 0; }
47.nav-btn {
48 background: rgba(0, 180, 216, 0.2);
49 color: #00b4d8;
50 border: 1px solid #00b4d8;
51 padding: 8px 16px;
52 border-radius: 6px;
53 cursor: pointer;
54}
55.nav-btn.active {
56 background: #00b4d8;
57 color: white;
58}
59
60.panel {
61 background: rgba(0, 255, 136, 0.05);
62 border: 2px solid #00ff88;
63 padding: 20px;
64 border-radius: 12px;
65}
66
67/* slide- classes (name="slide") */
68.slide-enter-active,
69.slide-leave-active {
70 transition: all 0.3s ease;
71}
72.slide-enter-from {
73 opacity: 0;
74 transform: translateX(30px);
75}
76.slide-leave-to {
77 opacity: 0;
78 transform: translateX(-30px);
79}
80</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. What happens when you add the attribute name='slide' to <Transition>?
2. What does mode='out-in' do in <Transition>?
These are 2 of 3 questions for this lesson. Solve the rest in the game.
Hands-on tasks in the game
- Code editor
Add <Transition name='slide'> and the corresponding slide-* CSS classes.
- Horizontal ordering
Arrange the correct Transition syntax with the out-in mode:
- Vertical ordering
Arrange the CSS classes in the order they appear during the element's leave animation:
- Code editor
Use <Transition mode='out-in'> for smooth switching between components.