---
title: Prefer Animating Transform and Opacity for Performance
impact: MEDIUM
impactDescription: Animating layout-triggering properties like height or margin causes expensive reflows and janky animations
type: best-practice
tags: [vue3, transition, animation, performance, css, transform, opacity]
---
# Prefer Animating Transform and Opacity for Performance
**Impact: MEDIUM** - When creating Vue transitions, you should prefer animating `transform` and `opacity` properties. These are GPU-accelerated and do not trigger expensive CSS layout recalculations. Animating properties like `height`, `width`, `margin`, `padding`, or `top`/`left` causes the browser to recalculate layout on every frame, resulting in janky, low-performance animations.
## Task Checklist
- [ ] Use `transform` for position and size animations (translate, scale, rotate)
- [ ] Use `opacity` for fade effects
- [ ] Avoid animating `height`, `width`, `margin`, `padding`, `top`, `left`, `right`, `bottom`
- [ ] If you must animate height, consider using `max-height` with a generous value or JavaScript-based solutions
- [ ] Use `will-change` sparingly and only when needed
**Problematic Code:**
```css
/* BAD: Animating height triggers layout recalculation every frame */
.slide-enter-active,
.slide-leave-active {
transition: height 0.3s ease;
}
.slide-enter-from,
.slide-leave-to {
height: 0;
}
.slide-enter-to,
.slide-leave-from {
height: 200px;
}
```
```css
/* BAD: Animating margin causes layout thrashing */
.slide-enter-active,
.slide-leave-active {
transition: margin-top 0.3s ease;
}
.slide-enter-from,
.slide-leave-to {
margin-top: -100%;
}
```
**Correct Code:**
```css
/* GOOD: Using transform and opacity - GPU accelerated */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
```
```css
/* GOOD: Using transform for slide animations */
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.slide-enter-from {
transform: translateX(-100%);
opacity: 0;
}
.slide-leave-to {
transform: translateX(100%);
opacity: 0;
}
```
```css
/* GOOD: Using scale instead of width/height */
.scale-enter-active,
.scale-leave-active {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.scale-enter-from,
.scale-leave-to {
transform: scale(0);
opacity: 0;
}
```
## When You Must Animate Height
If you absolutely need to animate height (e.g., accordion), consider these alternatives:
```vue
```
```vue
```
## Performance Comparison
| Property | Layout | Paint | Composite | Performance |
|----------|--------|-------|-----------|-------------|
| `transform` | No | No | Yes | Excellent |
| `opacity` | No | No | Yes | Excellent |
| `background-color` | No | Yes | Yes | Good |
| `width`/`height` | Yes | Yes | Yes | Poor |
| `margin`/`padding` | Yes | Yes | Yes | Poor |
| `top`/`left` | Yes | Yes | Yes | Poor |
## Reference
- [Vue.js Transition Documentation](https://vuejs.org/guide/built-ins/transition.html)
- [CSS Triggers](https://csstriggers.com/) - Reference for which properties trigger layout/paint
- [High Performance Animations](https://web.dev/animations-guide/)