Animations & Transitions
Add smooth motion to your UI with Tailwind's transition, duration, and easing utilities, plus the built-in animate-* keyframe animations.
Introduction
Small bits of motion — a button that darkens smoothly instead of snapping, a card that lifts slightly on hover, a spinner that rotates while data loads — make an interface feel responsive and considered. Tailwind covers both sides of motion: transition utilities for animating between two states (like normal and hover), and animate-* utilities for continuous, self-running keyframe animations.
- How transition-* enables smooth property changes.
- How duration-*, ease-*, and delay-* fine-tune the timing.
- The built-in animate-spin, animate-ping, animate-pulse, and animate-bounce utilities.
The transition Utilities
By default, CSS property changes happen instantly. Adding a transition-* class tells the browser to animate certain properties smoothly whenever they change, most commonly triggered by a hover, focus, or JavaScript-driven class toggle.
<button class="bg-blue-600 hover:bg-blue-700 transition-colors px-4 py-2 rounded-md text-white"> Hover Me</button>| Class | Animates |
|---|---|
| transition-none | Disables transitions entirely. |
| transition-colors | Background, text, and border color changes only. |
| transition-opacity | Opacity changes only. |
| transition-transform | Transform changes (scale, rotate, translate) only. |
| transition-all | Every animatable property — convenient, but can be less performant. |
| transition | A curated default set: color, background-color, border-color, opacity, box-shadow, and transform. |
Controlling Duration
duration-* sets how long the transition takes, in milliseconds. Without it, Tailwind defaults to 150ms — noticeable but fast.
<div class="transition-transform duration-300 hover:scale-105"> Scales up over 300ms on hover</div>
<div class="transition-transform duration-700 hover:scale-105"> Scales up more slowly, over 700ms</div>Controlling Easing
ease-* controls the acceleration curve of the transition — whether it starts fast and slows down, starts slow and speeds up, or moves at a constant rate.
| Class | Curve |
|---|---|
| ease-linear | Constant speed throughout. |
| ease-in | Starts slow, accelerates toward the end. |
| ease-out | Starts fast, decelerates toward the end — a natural feel for elements entering the screen. |
| ease-in-out | Slow start and slow end, faster in the middle. |
<div class="transition-opacity duration-500 ease-out hover:opacity-0"> Fades out smoothly, decelerating</div>Adding Delay
delay-* postpones the start of a transition, useful for staggering multiple elements so they do not all move at once.
<p class="transition-opacity duration-300 delay-100 hover:opacity-50">First</p><p class="transition-opacity duration-300 delay-300 hover:opacity-50">Second</p><p class="transition-opacity duration-300 delay-500 hover:opacity-50">Third</p>Combining with Hover and Focus
Transitions become genuinely useful once combined with interactive state variants. A card that lifts and gains a shadow on hover is a classic, effective pattern built entirely from utilities you already know.
<div class="p-6 bg-white rounded-lg shadow-sm border border-gray-200 transition-all duration-300 ease-out hover:shadow-lg hover:-translate-y-1"> <h3 class="font-semibold text-lg">Course Card</h3> <p class="text-gray-600 text-sm mt-2">Hover to see the lift effect.</p></div>Click Run to see what this code prints.
Built-in animate-* Utilities
Unlike transition-*, which needs a state change to trigger, animate-* utilities run continuously the moment the element is rendered, using predefined CSS @keyframes that ship with Tailwind.
<!-- Loading spinner --><svg class="animate-spin h-5 w-5 text-blue-600" viewBox="0 0 24 24">...</svg>
<!-- "Live" notification dot --><span class="relative flex h-3 w-3"> <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span> <span class="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span></span>
<!-- Skeleton loading placeholder --><div class="animate-pulse bg-gray-200 h-4 w-32 rounded"></div>
<!-- Attention-grabbing bounce --><div class="animate-bounce">↓</div>| Class | Effect |
|---|---|
| animate-spin | Continuous 360° rotation — ideal for loading spinners. |
| animate-ping | Scales up and fades out repeatedly — a radar-style pulse, often layered behind a solid dot. |
| animate-pulse | Gently fades opacity in and out — ideal for skeleton loading placeholders. |
| animate-bounce | Repeated vertical bounce — good for drawing attention to a scroll cue or new item. |
Common Mistakes
- Adding hover:scale-105 without transition-transform, causing the change to snap instantly instead of animating.
- Overusing transition-all on complex components, which can hurt performance by watching every animatable property instead of just the ones that actually change.
- Using animate-bounce or animate-ping on too many elements at once, creating a distracting, chaotic interface.
- Forgetting that animate-* utilities run immediately and continuously, unlike transition-*, which only activates on a state change.
Best Practices
- Prefer a specific transition-* utility (transition-colors, transition-transform) over transition-all when you know exactly what is changing.
- Keep hover transitions between 150ms and 300ms — long enough to feel smooth, short enough to feel responsive.
- Reserve continuous animate-* utilities for meaningful states like loading or live indicators, not decoration.
- Respect users who prefer reduced motion by pairing key animations with the motion-reduce: variant where appropriate.
Frequently Asked Questions
150ms, with Tailwind's default ease-in-out-like timing function — you can override either with duration-* and ease-*.
Yes — define @keyframes in your CSS and register a name under theme.extend.animation and theme.extend.keyframes in tailwind.config.js, then use it as a new animate-* utility.
It can, because the browser has to watch every animatable CSS property for changes rather than a specific subset, which is more expensive on complex or frequently updated elements.
Key Takeaways
- transition-* utilities animate a property between two states, typically triggered by hover, focus, or a class toggle.
- duration-*, ease-*, and delay-* fine-tune how a transition feels.
- animate-* utilities run continuous keyframe animations the moment the element renders.
- Pair transform utilities like scale and translate with transition-transform for smooth, natural-feeling motion.
Summary
You now know how to animate state changes with transition-*, fine-tune their timing, and use the built-in animate-* utilities for spinners, pulses, and live indicators. Next, you will learn about @layer, the directive that keeps custom CSS like this properly organized alongside Tailwind's own layers.