Example
Using the built-in <Transition> component to animate a <p> element as it is removed with v-if:
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
See more examples below.
Definition and Usage
The built-in <Transition> component is used to animate elements as they are added or removed with v-if, v-show, or with dynamic components.
The rules for how elements are animated is written inside automatically generated classes or JavaScript transition hooks. See tables below.
There can only be one element at the root level of the <Transition> component.
Props
| Prop | Description | |
|---|---|---|
| none | Default. | |
| appear | If set to true, the element is also animated as it is mounted for the first time. Default value is false. | |
| mode | mode="out-in" makes sure that the initial element leaves before the next element enters. mode="in-out" makes sure that the new element enters before the old element leaves. Default is that the old element leaves at the same time as the new element enters. | |
| name | Specify the name of a transition. If we have more than one transition we need to give them unique names to tell them apart. name="swirl" makes sure the CSS transition classes start with swirl- instead of the default prefix v-. | |
| css | Boolean. :css="false" tells the Vue compiler that no transition classes are used for this transition, only JavaScript hooks. With this prop set, the done() callback must be used inside the enter and leave hooks. | |
| type | Specify whether to wait for 'animation' or 'transition' to finish a transition. If both a CSS animation and a CSS transition is set, and this type prop is not set, Vue will detect the longest duration of these two and use that as the transition time. | |
| duration | Specify the length of the transition time for 'enter' and 'leave'. Default is to end when the CSS animation or CSS transition ends. Specific times can be defined like this :duration="{enter:2000, leave:1000 }", or like this duration="1000". | |
| enterFromClass enterActiveClass enterToClass appearFromClass appearActiveClass appearToClass leaveFromClass leaveActiveClass leaveToClass | Use these props to rename transition classes. Using one of these props like this enter-active-class="entering" means that this transition class can be referred to as .entering in CSS, instead of the default .v-enter-active. The convention is to use kebab-case for props in template to be in line with how attributes are written in HTML. |
CSS Transition Classes
When we use the <Transition> component, we automatically get six different CSS classes we can use to animate elements when they are added or removed.
These classes are active at different stages when elements are added (enter) or removed (leave):
| Transition Class | Description | |
|---|---|---|
| v-enter-from | Initial style of the element when the entering phase starts | |
| v-enter-active | The element's style during the entering phase | |
| v-enter-to | The element's style right at the end of the entering phase | |
| v-leave-from | Initial style of the element when the leaving phase starts | |
| v-leave-active | The element's style during the leaving phase | |
| v-leave-to | The element's style right at the end of the leaving phase |
JavaScript Transition Hooks
The transition classes above corresponds to events that we can hook into to run JavaScript code.
| JavaScript Event | Description | |
|---|---|---|
| before-enter | Called right at the start of the enter phase | |
| enter | Called after the 'before-enter' hook, during the enter phase | |
| after-enter | Called right at the end of the enter transition | |
| enter-cancelled | Called if the enter transition is cancelled | |
| before-leave | Called right at the start of the leave phase | |
| leave | Called after the 'before-leave' hook, during the leave phase | |
| after-leave | Called right at the end of the leave transition | |
| leave-cancelled | This is only called if v-show is used and the leave phase is cancelled |
More examples
Example 1
A <p> element slides in and out when it is toggled.
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.v-enter-from {
opacity: 0;
translate: -100px 0;
}
.v-enter-to {
opacity: 1;
translate: 0 0;
}
.v-leave-from {
opacity: 1;
translate: 0 0;
}
.v-leave-to {
opacity: 0;
translate: 100px 0;
}
p {
background-color: lightgreen;
display: inline-block;
padding: 10px;
transition: all 0.5s;
}
</style>
Example 2
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.v-enter-active {
background-color: lightgreen;
animation: added 1s;
}
.v-leave-active {
background-color: lightcoral;
animation: added 1s reverse;
}
@keyframes added {
from {
opacity: 0;
translate: -100px 0;
}
to {
opacity: 1;
translate: 0 0;
}
}
p {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
</style>
A <p> element has separate background colors during 'enter' and 'leave'
Example 3
<p> elements are animated differently, using the name prop to differentiate the <Transition> components.
<template>
<h1>Add/Remove <p> Tag</h1>
<p>The second transition in this example has the name prop "swirl", so that we can keep the transitions apart with different class names.</p>
<hr>
<button @click="this.p1Exists = !this.p1Exists">{{btn1Text}}</button><br>
<Transition>
<p v-if="p1Exists" id="p1">Hello World!</p>
</Transition>
<hr>
<button @click="this.p2Exists = !this.p2Exists">{{btn2Text}}</button><br>
<Transition name="swirl">
<p v-if="p2Exists" id="p2">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
p1Exists: false,
p2Exists: false
}
},
computed: {
btn1Text() {
if(this.p1Exists) {
return 'Remove';
}
else {
return 'Add';
}
},
btn2Text() {
if(this.p2Exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.v-enter-active {
background-color: lightgreen;
animation: added 1s;
}
.v-leave-active {
background-color: lightcoral;
animation: added 1s reverse;
}
@keyframes added {
from {
opacity: 0;
translate: -100px 0;
}
to {
opacity: 1;
translate: 0 0;
}
}
.swirl-enter-active {
animation: swirlAdded 1s;
}
.swirl-leave-active {
animation: swirlAdded 1s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1, #p2 {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p2 {
background-color: lightcoral;
}
</style>
Example 4
The after-enter event triggers a <div> element to be displayed.
<template>
<h1>JavaScript Transition Hooks</h1>
<p>This code hooks into "after-enter" so that after the initial animation is done, a method runs that displays a red div.</p>
<button @click="pVisible=true">Create p-tag!</button><br>
<Transition @after-enter="onAfterEnter">
<p v-show="pVisible" id="p1">Hello World!</p>
</Transition>
<br>
<div v-show="divVisible">This appears after the "enter-active" phase of the transition.</div>
</template>
<script>
export default {
data() {
return {
pVisible: false,
divVisible: false
}
},
methods: {
onAfterEnter() {
this.divVisible = true;
}
}
}
</script>
<style>
.v-enter-active {
animation: swirlAdded 1s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1, div {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p1 {
background-color: lightgreen;
}
div {
background-color: lightcoral;
}
</style>
Example 5
A toggle button triggers the enter-cancelled event.
<template>
<h1>The 'enter-cancelled' Event</h1>
<p>Click the toggle button again before the enter animation is finished to trigger the 'enter-cancelled' event.</p>
<button @click="pVisible=!pVisible">Toggle</button><br>
<Transition @enter-cancelled="onEnterCancelled">
<p v-if="pVisible" id="p1">Hello World!</p>
</Transition>
<br>
<div v-if="divVisible">You interrupted the "enter-active" transition.</div>
</template>
<script>
export default {
data() {
return {
pVisible: false,
divVisible: false
}
},
methods: {
onEnterCancelled() {
this.divVisible = true;
}
}
}
</script>
<style>
.v-enter-active {
animation: swirlAdded 2s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 720deg;
scale: 1;
}
}
#p1, div {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p1 {
background-color: lightgreen;
}
div {
background-color: lightcoral;
}
</style>
Example 6
The appear prop starts the <p> element animation right after the page is loaded.
<template>
<h1>The 'appear' Prop</h1>
<p>The 'appear' prop starts the animation when the p tag below is rendered for the first time as the page opens. Without the 'appear' prop, this example would have had no animation.</p>
<Transition appear>
<p id="p1">Hello World!</p>
</Transition>
</template>
<style>
.v-enter-active {
animation: swirlAdded 1s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1 {
display: inline-block;
padding: 10px;
border: dashed black 1px;
background-color: lightgreen;
}
</style>
Example 7
Flip through images with animation on 'enter' and 'leave'. A new image is added before the old image is removed.
<template>
<h1>Transition Between Elements</h1>
<p>Click the button to get a new image.</p>
<p>The new image is added before the previous is removed. We will fix this in the next example with mode="out-in".</p>
<button @click="newImg">Next image</button><br>
<Transition>
<img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
<img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
<img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
<img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
<img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
</Transition>
</template>
<script>
export default {
data() {
return {
imgActive: 'pizza',
imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
indexNbr: 0
}
},
methods: {
newImg() {
this.indexNbr++;
if(this.indexNbr >= this.imgs.length) {
this.indexNbr = 0;
}
this.imgActive = this.imgs[this.indexNbr];
}
}
}
</script>
<style>
.v-enter-active {
animation: swirlAdded 1s;
}
.v-leave-active {
animation: swirlAdded 1s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
img {
width: 100px;
margin: 20px;
}
img:hover {
cursor: pointer;
}
</style>
Example 8
Flip through images with animation on 'enter' and 'leave'. The mode="out-in" prevents new images to be added until the old image is removed.
<template>
<h1>mode="out-in"</h1>
<p>Click the button to get a new image.</p>
<p>With mode="out-in", the next image is not added until the current image is removed. Another difference from the previous example, is that here we use computed prop instead of a method.</p>
<button @click="indexNbr++">Next image</button><br>
<Transition mode="out-in">
<img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
<img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
<img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
<img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
<img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
</Transition>
</template>
<script>
export default {
data() {
return {
imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
indexNbr: 0
}
},
computed: {
imgActive() {
if(this.indexNbr >= this.imgs.length) {
this.indexNbr = 0;
}
return this.imgs[this.indexNbr];
}
}
}
</script>
<style>
.v-enter-active {
animation: swirlAdded 0.7s;
}
.v-leave-active {
animation: swirlAdded 0.7s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
img {
width: 100px;
margin: 20px;
}
img:hover {
cursor: pointer;
}
</style>
Example 9
Switching between components is animated.
<template>
<h1>Transition with Dynamic Components</h1>
<p>The Transition component wraps around the dynamic component so that the switching can be animated.</p>
<button @click="toggleValue = !toggleValue">Switch component</button>
<Transition mode="out-in">
<component :is="activeComp"></component>
</Transition>
</template>
<script>
export default {
data () {
return {
toggleValue: true
}
},
computed: {
activeComp() {
if(this.toggleValue) {
return 'comp-one'
}
else {
return 'comp-two'
}
}
}
}
</script>
<style>
.v-enter-active {
animation: slideIn 0.5s;
}
@keyframes slideIn {
from {
translate: -200px 0;
opacity: 0;
}
to {
translate: 0 0;
opacity: 1;
}
}
.v-leave-active {
animation: slideOut 0.5s;
}
@keyframes slideOut {
from {
translate: 0 0;
opacity: 1;
}
to {
translate: 200px 0;
opacity: 0;
}
}
#app {
width: 350px;
margin: 10px;
}
#app > div {
border: solid black 2px;
padding: 10px;
margin-top: 10px;
}
</style>
Example 10
Switching between components is animated.
<template>
<h1>The :css="false" Prop</h1>
<p>With the 'css' prop set to 'false', we tell the compiler that JavaScript hooks are used instead of CSS transition classes.</p>
<p>When we use :css="false", we must call done() inside the 'enter' and the 'leave' hooks, to tell the browser when those transitions are finished.</p>
<button @click="pVisible=!pVisible">Toggle</button>
<div>
<Transition
:css="false"
@enter="onEnter"
@after-enter="onAfterEnter"
@before-leave="onBeforeLeave"
@leave="onLeave"
>
<p
v-if="pVisible"
id="p1">
Hello World!
</p>
</Transition>
</div>
</template>
<script>
export default {
data() {
return {
pVisible: false
}
},
methods: {
onEnter(el,done) {
let pos = 0;
window.requestAnimationFrame(frame);
function frame() {
if (pos > 150) {
done();
} else {
pos++;
el.style.left = pos + "px";
window.requestAnimationFrame(frame);
}
}
},
onAfterEnter(el) {
el.style.backgroundColor = "yellow";
},
onBeforeLeave(el) {
el.style.backgroundColor = "lightgreen";
},
onLeave(el,done) {
let pos = 150;
window.requestAnimationFrame(frame);
function frame() {
if (pos < 0) {
done();
}
else {
pos--;
el.style.left = pos + "px";
window.requestAnimationFrame(frame);
}
}
}
}
}
</script>
<style>
#p1 {
position: absolute;
padding: 10px;
border: dashed black 1px;
background-color: lightgreen;
}
#app > div {
position: relative;
background-color: coral;
width: 300px;
height: 300px;
border: dashed black 1px;
margin-top: 20px;
}
</style>
Example 11
Using the enterActiveClass prop to rename the 'v-enter-active' CSS class to 'entering'.
<template>
<h1>The 'enterActiveClass' Prop</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition enter-active-class="entering">
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.entering {
background-color: lightgreen;
animation: added 1s;
}
.v-leave-active {
background-color: lightcoral;
animation: added 1s reverse;
}
@keyframes added {
from {
opacity: 0;
translate: -100px 0;
}
to {
opacity: 1;
translate: 0 0;
}
}
p {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
</style>
Related Pages
- Vue Tutorial: Vue Animations
- Vue Tutorial: Vue Animations with v-for
- Vue Reference: Vue <TransitionGroup> Component