Skip to main content
Category:

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.

There are six classes applied for enter/leave transitions.

  1. v-enter: Starting state for enter. Added before element is inserted, removed one frame after element is inserted.

  2. v-enter-active: Active state for enter. Applied during the entire entering phase. Added before element is inserted, removed when transition/animation finishes. This class can be used to define the duration, delay and easing curve for the entering transition.

  3. v-enter-toOnly available in versions 2.1.8+. Ending state for enter. Added one frame after element is inserted (at the same time v-enter is removed), removed when transition/animation finishes.

  4. v-leave: Starting state for leave. Added immediately when a leaving transition is triggered, removed after one frame.

  5. v-leave-active: Active state for leave. Applied during the entire leaving phase. Added immediately when leave transition is triggered, removed when the transition/animation finishes. This class can be used to define the duration, delay and easing curve for the leaving transition.

  6. v-leave-toOnly available in versions 2.1.8+. Ending state for leave. Added one frame after a leaving transition is triggered (at the same time v-leave is removed), removed when the transition/animation finishes.

 

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
  • .block-enter-to
  • .block-leave-to

 

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.

 

Dynamic naming

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

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.

 

Mixing animations and transitions

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>

 

Using the Animate.css library within the transition tag

Animate.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.

  1. Install the NPM package or add the CDN of animate.css to the index.html
    npm install animate.css --save
  2. Instead of adding a name attribute to the transition tag, add two new attributes: .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>

 

Transitioning between multiple elements

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.

  1. Within the transition tag, give it a mode attribute. The mode can either be out-in or in-outOut-in means the current element being displayed goes out before a new one comes in, and vice versa for in-out
  2. Then we give each of the two elements keys, this acts as a unique identifier for the transition tag wrapper to use for them. That’s it! They’ll switch places smoothly without any issue.
    
    <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>

 

Using transition-group to animate lists

Using 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>

Riadh Rahmi

Senior Web Developer PHP/Drupal & Laravel

I am a senior web developer, I have experience in planning and developing large scale dynamic web solutions especially in Drupal & Laravel.

Web Posts

Search

Page Facebook