LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2717 min read

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.

What You Will Learn
  • 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>
ClassAnimates
transition-noneDisables transitions entirely.
transition-colorsBackground, text, and border color changes only.
transition-opacityOpacity changes only.
transition-transformTransform changes (scale, rotate, translate) only.
transition-allEvery animatable property — convenient, but can be less performant.
transitionA 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.

ClassCurve
ease-linearConstant speed throughout.
ease-inStarts slow, accelerates toward the end.
ease-outStarts fast, decelerates toward the end — a natural feel for elements entering the screen.
ease-in-outSlow 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>
Rendered Result

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>
ClassEffect
animate-spinContinuous 360° rotation — ideal for loading spinners.
animate-pingScales up and fades out repeatedly — a radar-style pulse, often layered behind a solid dot.
animate-pulseGently fades opacity in and out — ideal for skeleton loading placeholders.
animate-bounceRepeated vertical bounce — good for drawing attention to a scroll cue or new item.

Common Mistakes

Avoid These 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.

Next Lesson →

Reusable Components with @layer