Overview
Vuetify provides a set of reusable transition components for animating elements. These transitions follow Material Design motion principles and can be used with Vue’s <Transition> and <TransitionGroup> components.
import {
VFadeTransition,
VScaleTransition,
VSlideXTransition,
VSlideYTransition,
VExpandTransition,
// ... and more
} from 'vuetify/components'
Available Transitions
Fade Transitions
Simple opacity fade in/out
Scale Transitions
Scale from/to center with fade
Scale transition optimized for FAB buttons (from center, out-in mode)
Slide Transitions
Slide horizontally from left
Slide horizontally from right
Slide vertically from top
Slide vertically from bottom
Scroll Transitions
Scroll effect horizontally from left
VScrollXReverseTransition
Scroll effect horizontally from right
Scroll effect vertically from top
VScrollYReverseTransition
Scroll effect vertically from bottom
Dialog Transitions
Default dialog transition
Expand Transitions
Vertical expand/collapse transition
Horizontal expand/collapse transition
Shared Props
All transition components support these props:
Use TransitionGroup instead of Transition (for lists)
Hide element with display: none during leave transition
Position element absolutely during leave transition
Transition mode (‘in-out’, ‘out-in’, or ‘default’)
Transform origin for the transition
Usage Examples
Basic Fade
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<template>
<div>
<v-btn @click="show = !show">Toggle</v-btn>
<v-fade-transition>
<v-card v-if="show">
Content fades in and out
</v-card>
</v-fade-transition>
</div>
</template>
Scale Transition
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<template>
<v-scale-transition>
<v-icon v-if="show" size="64">
mdi-check-circle
</v-icon>
</v-scale-transition>
</template>
Slide Transitions
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<template>
<div>
<v-slide-x-transition>
<v-card v-if="show">Slides from left</v-card>
</v-slide-x-transition>
<v-slide-y-transition>
<v-card v-if="show">Slides from top</v-card>
</v-slide-y-transition>
</div>
</template>
List with TransitionGroup
<script setup>
import { ref } from 'vue'
const items = ref(['Item 1', 'Item 2', 'Item 3'])
function removeItem(index: number) {
items.value.splice(index, 1)
}
</script>
<template>
<v-list>
<v-slide-y-transition group>
<v-list-item
v-for="(item, i) in items"
:key="item"
>
{{ item }}
<template #append>
<v-btn icon size="small" @click="removeItem(i)">
<v-icon>mdi-close</v-icon>
</v-btn>
</template>
</v-list-item>
</v-slide-y-transition>
</v-list>
</template>
Expand Transition
<script setup>
import { ref } from 'vue'
const expanded = ref(false)
</script>
<template>
<v-card>
<v-card-title @click="expanded = !expanded">
Click to expand
<v-icon>{{ expanded ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
</v-card-title>
<v-expand-transition>
<div v-if="expanded">
<v-divider />
<v-card-text>
This content expands and collapses smoothly
</v-card-text>
</div>
</v-expand-transition>
</v-card>
</template>
Custom Origin
<template>
<v-scale-transition origin="top left">
<v-card v-if="show">
Scales from top left corner
</v-card>
</v-scale-transition>
</template>
Mode: Out-In
<script setup>
import { ref } from 'vue'
const current = ref('first')
</script>
<template>
<div>
<v-btn @click="current = current === 'first' ? 'second' : 'first'">
Switch
</v-btn>
<v-fade-transition mode="out-in">
<v-card v-if="current === 'first'" key="first">
First card
</v-card>
<v-card v-else key="second">
Second card
</v-card>
</v-fade-transition>
</div>
</template>
Staggered List Animation
<script setup>
import { ref } from 'vue'
const items = ref(['Item 1', 'Item 2', 'Item 3', 'Item 4'])
</script>
<template>
<v-list>
<v-slide-x-transition
group
hide-on-leave
>
<v-list-item
v-for="(item, i) in items"
:key="item"
:style="{ transitionDelay: `${i * 50}ms` }"
>
{{ item }}
</v-list-item>
</v-slide-x-transition>
</v-list>
</template>
FAB Button
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<template>
<v-fab-transition>
<v-btn
v-if="show"
icon
color="primary"
size="large"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-fab-transition>
</template>
Carousel Transitions
<script setup>
import { ref } from 'vue'
const current = ref(0)
const images = ['/img1.jpg', '/img2.jpg', '/img3.jpg']
</script>
<template>
<div>
<v-slide-x-transition mode="out-in">
<v-img :key="current" :src="images[current]" />
</v-slide-x-transition>
<v-btn @click="current = (current - 1 + images.length) % images.length">
Previous
</v-btn>
<v-btn @click="current = (current + 1) % images.length">
Next
</v-btn>
</div>
</template>
Conditional Messages
<script setup>
import { ref } from 'vue'
const message = ref<string | null>(null)
function showSuccess() {
message.value = 'Success!'
setTimeout(() => message.value = null, 3000)
}
function showError() {
message.value = 'Error!'
setTimeout(() => message.value = null, 3000)
}
</script>
<template>
<div>
<v-slide-y-transition>
<v-alert v-if="message" :type="message.includes('Success') ? 'success' : 'error'">
{{ message }}
</v-alert>
</v-slide-y-transition>
</div>
</template>
Creating Custom Transitions
CSS Transition
import { createCssTransition } from 'vuetify/components/transitions'
export const VMyTransition = createCssTransition(
'my-transition',
'center center', // transform origin
'out-in' // mode
)
JavaScript Transition
import { createJavascriptTransition } from 'vuetify/components/transitions'
export const VMyJsTransition = createJavascriptTransition(
'my-js-transition',
{
onBeforeEnter(el: HTMLElement) {
el.style.opacity = '0'
},
onEnter(el: HTMLElement, done: () => void) {
el.style.transition = 'opacity 0.3s'
el.style.opacity = '1'
setTimeout(done, 300)
},
onLeave(el: HTMLElement, done: () => void) {
el.style.transition = 'opacity 0.3s'
el.style.opacity = '0'
setTimeout(done, 300)
}
}
)
Accessibility
- Transitions respect
prefers-reduced-motion media query
- Set
disabled prop to disable transitions for accessibility
- Use semantic HTML within transitions
- Ensure content is accessible during transition
Performance Tips
- Use CSS transforms (translate, scale) instead of position changes
- Avoid animating layout properties (width, height, margin)
- Use
will-change CSS property sparingly
- Prefer
v-show over v-if for frequent toggles
- Use
hideOnLeave for better performance with complex content
- All transitions use CSS for smooth 60fps animations
- Expand transitions use JavaScript for dynamic height calculation
- Group mode requires unique
:key on each item
- Transition duration is typically 150-300ms
- Origin affects scale and rotation transformations
See Also