Vue <Transition> Component
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>
Run Example »
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. | Run Example » |
appear | If set to true , the element is also animated as it is mounted for the first time. Default value is false . |
Run Example » |
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. |
Run Example » |
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- . |
Run Example » |
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. |
Run Example » |
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 |
Run Example » |
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 | Run Example » |
v-enter-active | The element's style during the entering phase | Run Example » |
v-enter-to | The element's style right at the end of the entering phase | Run Example » |
v-leave-from | Initial style of the element when the leaving phase starts | Run Example » |
v-leave-active | The element's style during the leaving phase | Run Example » |
v-leave-to | The element's style right at the end of the leaving phase | Run Example » |
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 | Run Example » |
after-enter | Called right at the end of the enter transition | Run Example » |
enter-cancelled | Called if the enter transition is cancelled | Run Example » |
before-leave | Called right at the start of the leave phase | Run Example » |
leave | Called after the 'before-leave' hook, during the leave phase | Run Example » |
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>
Run Example »
Example 2
A <p>
element has separate background colors during 'enter' and 'leave'
<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>
Run Example »
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>
Run Example »
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>
Run Example »
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>
Run Example »
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>
Run Example »
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>
Run Example »
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>
Run Example »
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>
Run Example »
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>
Run Example »
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>
Run Example »
Related Pages
Vue Tutorial: Vue Animations
Vue Tutorial: Vue Animations with v-for
Vue Reference: Vue <TransitionGroup> Component