Adding transitions and animations to your web applications can help capture your users’ attention and jazz up the user experience. Even subtle effects and movements can lend your page a sense of action, especially if they play as the website is loaded.
To put what I’m saying into context, take a look at these login pages below (source: japa).
Which of these pages is more visually appealing? I’m sure most people would go with the second option. The effect is pretty minimal, but it provides more visual stimulation than the first, static page. On the other side of the coin, animations could be disturbing if excessive or too fast.
Transitions in Vue.js are particularly in cases where you need to attach or remove an element from the DOM, whether it’s due to routing, conditional statements, or whatever affects the element’s attachment to the DOM.
Consider the case below. We have our component and we’re using conditionals (v-if
/v-else
) to toggle the element from the webpage.
<template> <div> <div class="container"> <button @click="display = !display">Switch</button> <div class="item" v-if="display">1</div> </div> </div> </template> <style scoped> body { align-content: center; } .container { display: grid; grid-gap: 20px; width: 500px; margin: 0 auto; } .item { background-color: blue; height: 100px; } </style>
Then we can add the data attributes to bind to the HTML form.
[...] <script> export default { data () { return { display : false } } } </script>
To create a transition on this element as it’s been toggled, the first step is to wrap the element in a <transition>
tag. Next, give it a unique name
to be used when creating the transition classes.
This toggling doesn’t have to be due to v-if
/v-else
; it could be a dynamic component or anything else. Just wrap the element with a <transition>
tag and it’ll work the same.
<button @click="display = !display">Switch</button> <transition name="block"> <div class="item" v-if="display">1</div> </transition>
The transition tag only allows one element (or group of elements under one div
wrapper) to display at any given time.
After inputting the name, this is where the transition classes come in. Transition classes are default classes that ship with Vue to determine when and how to attach or remove an element from the DOM.
.v-enter
— This CSS class is only attached for one frame at the beginning of the transition while attaching to the DOM.v-enter-active
— This is where the process of attaching to the DOM occurs after the first frame.v-leave
— As with .v-enter
, this is attached for the one frame at the removal of the DOM.v-leave-active
— This is the class that holds the style for the transitions that remove the element from the DOM.In a real-life application, you’d replace the .v
in front of the classes with the name
of the transition you want it to affect. So your component would be structured like this:
.block-enter
.block-enter-active
.block-leave
.block-leave-active
Let’s have spin up a quick example of a fade transition using our initial component. The transition classes will look like this:
.block-enter { Opacity: 0; } .block-enter-active { transition : opacity 2s; } .block-leave { } .block-leave-active { transition: opacity 2s; Opacity: 0; }
In .block-enter
, the opacity is 0
by default. The element gradually displays itself with .block-enter-active
with an opacity of 2s
.
With .block-leave
, the opacity is 1
by default because, at that moment, the element is still attached to the DOM. With .block-leave-active
, it gradually disappears until the opacity ends up being 0
.
The naming of the transition element doesn’t always have to be hardcoded, depending on your code or what you want the transition to look like. You can also bind the names of the transition, so it can be dynamic and not neccesarily hardcoded. It will be binded to a property and can be changed with a dropdown menu or button — again, depending on what action you wish to take. This is useful in cases where you have two separate transition styles and you need to switch between them.
In this instance below, we’ll use a dropdown menu.
<template> <div> <div class="container"> <select v-model="animate"> <option value="block">Fade</option> <option value="gradualFade">Slower fade</option> </select> <button @click="display = !display">Switch</button> <transition :name="animate"> <div class="item" v-if="display">1</div> </transition> </div> </div> </template> [...] <style scoped> body { align-content: center; } .container { display: grid; grid-gap: 20px; width: 500px; margin: 0 auto; } .item { background-color: blue; height: 100px; } .block-enter { Opacity: 0; } .block-enter-active { transition : opacity 2s; } .block-leave { } .block-leave-active { Transition: opacity 2s; Opacity: 0; } .gradualFade-enter { Opacity: 0; } .gradualFade-enter-active { transition : opacity 4s; } .gradualFade-leave { } .gradualFade-leave-active { Transition: opacity 4s; Opacity: 0; } </style> <script> export default { data () { return { display : false, animate : 'block' } } } </script>
Animated properties can be used to handle animations, just like transition properties. They use keyframes the same way it works in traditional CSS.
Let’s recreate the fade effect, but this time with animated properties.
<template> <div> <div class="container"> <button @click="display = !display">Switch</button> <transition name="block"> <div class="item" v-if="display">1</div> </transition> </div> </div> </template> <style scoped> body { align-content: center; } .container { display: grid; grid-gap: 20px; width: 500px; margin: 0 auto; } .item { background-color: blue; height: 100px; } .block-enter { } .block-enter-active { animation : fade-in 1s } .block-leave { } .block-leave-active { animation : fade-out 1s } @keyframes fade-in { from { opacity : 0 } to { opacity : 1; } } @keyframes fade-out { from { opacity : 1; } to { opacity : 0; } } </style> <script> export default { data () { return { display : false } } } </script>
Using the same component we set up before, we can change the styles, this time using keyframes and adding the animation name and timing to the transition classes.
You can use both animations and transitions on the same element, which can be especially useful when creating effects. To see this in action, let’s create slide and fade effects that execute simultaneously using both transitions and animations.
When being attached to the DOM, the box slides in from below while fading in simultaneously. When being removed from the DOM, it slides up and fades out.
<template> <div> <div class="container"> <button @click="display = !display">Switch</button> <transition name="block"> <div class="item" v-if="display">1</div> </transition> </div> </div> </template> <style scoped> body { align-content: center; } .container { display: grid; grid-gap: 20px; width: 500px; margin: 0 auto; } .item { background-color: blue; height: 100px; margin-bottom: 10px; } .block-enter { opacity: 0; } .block-enter-active { animation : slide-in 1s ease-out forwards; transition : opacity 1s; } .block-leave { opacity: 1; } .block-leave-active { animation : slide-out 1s ease-out forwards; transition : opacity 1s; opacity: 0; } @keyframes slide-in { from { transform: translateY(20px); } to { transform: translateY(0); } } @keyframes slide-out { from { transform: translateY(0); } to { transform: translateY(-20px); } } </style> <script> export default { data () { return { display : false } } } </script>
We’re still using the same component. In the styles, we added separate animation keyframes for attaching and detaching the element.
For the slide-in
keyframe, since it slides in from below, we added transform: translateY(20px)
to transform: translateY(0)
.
For the slide-out
keyframe, it’s sliding out upward, so we added transform : translateY(-20px)
to transform: translateY(0)
.
Back to the transition classes; we gave the block-enter
frame an opacity of 0
.
.block-enter { Opacity: 0; }
In .block-enter-active
, it gradually changes the opacity to 1
(it’s 1
by default since it’s attached to the DOM), all within the 1s
. The slide-in
animation takes effect easing out with forwards
to ensure it lands properly.
.block-enter-active { animation : slide-in 1s ease-out forwards; transition : opacity 1s; }
To remove from the DOM, the animation is added to .block-leave-active
. It slides out and the opacity changes to 0
, still within 1s
.
.block-leave-active { animation : slide-out 1s ease-out forwards; transition : opacity 1s; opacity: 0; }
If the timing used within your animation and transition in any of the transition classes (attaching or dettaching) is different, there won’t be a balance and it’ll change unevenly. If that’s the case, add type
to the transition class and tell it the exact timing to follow, whether transition or animation.
<transition name="block" type="animation"> <div class="item" key="1" v-if="display">1</div> </transition>
If you want the animation to work on loading the page, you can change the display parameter to true
and add appear
to the transition
tag.
<transition name="block" type="animation" appear> <div class="item" key="1" v-if="display">1</div> </transition>
Animate.css
library within the transition tagAnimate.css
is a popular library for animations with which you might already be conversant. It’s useful even with Vue.
Libraries like Animate.css
come with their own CSS classes aside from the transition classes we’ve been using. To handle that, we’ll take the following steps.
animate.css
to the index.html
npm install animate.css --save
.enter-active-class
and .leave-active-class
. This is where you’ll add the classes from animate.css
for attaching and removing from the DOM
<transition name="block" enter-active-class="animate__animated animate__bounce" leave-active-class="animate__animated animate__pulse" > <div class="item" key="1" v-if="display">1</div> </transition>
If you need to have two elements within a transition tag and display just one at a time due to a conditional v-if
, take the following steps.
out-in
or in-out
. Out-in
means the current element being displayed goes out before a new one comes in, and vice versa for in-out
<transition name="block" mode="out-in"> <div class="item" key="1" v-if="display">1</div> <div class="item" key="2" v-if="!display">2</div> </transition>
transition-group
to animate listsUsing transition-group
is the same as using the transition-group
tag, but for a list of elements displaying at once instead of just one. It’s useful in cases where you just want to loop through data you’re fetching from an API, maybe for a dashboard or something of that nature.
One fun fact is that transition-group
is actually rendered to the DOM as a <span>
, but on the other hand, transition
isn’t rendered to the DOM.
Below is a transition-group
tag wrapping some elements. We’re still using the same transition classes we used with the transition
tag, but this time each separate element has a key. This key is used with the transition
tag. It allows the transition-group
to identify each element and can animate them as needed.
<template> <div> <div class="container"> <button @click="display = !display">Switch</button> <transition-group name="block"> <div class="item" key="1" v-if="display">1</div> <div class="item" key="2" v-if="display">2</div> <div class="item" key="3" v-if="display">3</div> </transition-group> </div> </div> </template> [...] <style scoped> body { align-content: center; } .container { display: grid; grid-gap: 20px; width: 500px; margin: 0 auto; } .item { background-color: blue; height: 100px; margin-bottom: 10px; } .block-enter { opacity: 0; } .block-enter-active { animation : slide-in 1s ease-out forwards; transition : opacity 1s; } .block-leave { opacity: 1; } .block-leave-active { animation : slide-out 1s ease-out forwards; transition : opacity 1s; opacity: 0; } @keyframes slide-in { from { transform: translateY(20px); } to { transform: translateY(0); } } @keyframes slide-out { from { transform: translateY(0); } to { transform: translateY(-20px); } } </style> <script> export default { data () { return { display : false } } } </script>
Before you go, it’s worth noting that there are animation libraries such as vue2-animate that can help you create seamless animations in Vue without having to write the code by yourself.
Feel free to reference the GitHub repo if you’re not clear on the code. There’s also a demo site site where you can check out all the effects.
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.