---
name: motion-design-patterns
description: Framer Motion (Motion) animation patterns for React — springs, staggers, layout animations, micro-interactions, scroll effects, and page transitions. Use when building or improving UI animations, adding polish, or making interfaces feel premium.
---
# Motion Design Patterns
Framer Motion (Motion) patterns for React — springs, staggers, layout animations, micro-interactions, scroll-triggered effects, and exit animations. The #1 differentiator between generic and polished UI.
## When to Use
- Building or improving UI animations in a React project
- User asks for "polish", "delight", "micro-interactions", or "make it feel good"
- Adding entrance/exit animations, hover effects, or page transitions
- Making lists, cards, modals, or navigation feel premium
- User references Magic UI, Motion Primitives, or Framer Motion
## Core Philosophy
- **Motion should be purposeful.** Every animation should communicate something — state change, hierarchy, spatial relationship, or feedback.
- **Less is more.** One well-tuned spring beats five competing animations.
- **Performance first.** Animate `transform` and `opacity` only. Never animate `width`, `height`, `top`, `left`, or `margin`.
- **Consistency matters.** Use the same spring configs throughout a project.
## Dependencies
```bash
npm install motion
```
Import: `import { motion, AnimatePresence, stagger, useScroll, useTransform } from "motion/react"`
Note: The package was renamed from `framer-motion` to `motion` in late 2024. Both work, but `motion` is the current package.
---
## Spring Configurations
Springs feel more natural than easing curves. Use these as your defaults:
### Recommended Defaults
```tsx
// Snappy — buttons, toggles, small elements
const snappy = { type: "spring", stiffness: 500, damping: 30 }
// Smooth — cards, panels, modals
const smooth = { type: "spring", stiffness: 300, damping: 25 }
// Gentle — page transitions, large elements
const gentle = { type: "spring", stiffness: 200, damping: 20 }
// Bouncy — playful UI, notifications, badges
const bouncy = { type: "spring", stiffness: 400, damping: 15 }
```
### Quick Reference
| Feel | stiffness | damping | Use for |
|------|-----------|---------|---------|
| Snappy | 500 | 30 | Buttons, toggles, chips |
| Smooth | 300 | 25 | Cards, panels, modals |
| Gentle | 200 | 20 | Page transitions, heroes |
| Bouncy | 400 | 15 | Notifications, badges, fun UI |
### Rules of Thumb
- **Higher stiffness** = faster animation
- **Lower damping** = more bounce
- **damping ratio < 1** = will overshoot (bounce)
- For **no bounce**, set damping ≥ 2 × √stiffness
---
## Pattern 1: Fade + Rise Entrance
The bread-and-butter entrance animation. Element fades in while sliding up slightly.
```tsx
Content here
```
**When to use:** Cards, sections, any content appearing on the page.
**Anti-pattern:** Don't use `y: 100` or large values — subtle (12–24px) feels premium, large feels janky.
---
## Pattern 2: Staggered List
Children animate in one after another. The cascade effect that makes lists feel alive.
```tsx
const container = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.08,
delayChildren: 0.1,
},
},
}
const item = {
hidden: { opacity: 0, y: 16 },
visible: {
opacity: 1,
y: 0,
transition: { type: "spring", stiffness: 300, damping: 25 },
},
}
{items.map((i) => (
{i.content}
))}
```
**Timing guide:**
- 3–5 items: `staggerChildren: 0.1`
- 6–12 items: `staggerChildren: 0.06`
- 12+ items: `staggerChildren: 0.03` (or animate as a group)
**Anti-pattern:** Don't stagger more than ~15 items individually — it feels slow. Group them or use a wave effect.
---
## Pattern 3: Layout Animations
Automatically animate between layout states. Motion's killer feature.
```tsx
{isExpanded ? : }
```
### Shared Layout (Tabs, Active Indicators)
```tsx
{tabs.map((tab) => (
))}
```
**When to use:** Tab indicators, expanding cards, reordering lists, filtering grids.
**Performance tip:** Add `layout="position"` if you only need to animate position (not size). It's cheaper.
---
## Pattern 4: Exit Animations (AnimatePresence)
Elements animate out before being removed from the DOM.
```tsx
{isVisible && (
Modal content
)}
```
### AnimatePresence modes
| Mode | Behavior | Use for |
|------|----------|---------|
| `"sync"` (default) | Enter and exit at the same time | Crossfade effects |
| `"wait"` | Wait for exit to finish before entering | Page transitions, modals |
| `"popLayout"` | Exiting element pops out of layout flow | Lists where items are removed |
---
## Pattern 5: Hover & Tap Micro-interactions
```tsx
Click me
```
### Hover Patterns by Element Type
| Element | whileHover | whileTap |
|---------|-----------|---------|
| Button (primary) | `{ scale: 1.02 }` | `{ scale: 0.98 }` |
| Card | `{ y: -4, boxShadow: "0 8px 30px rgba(0,0,0,0.12)" }` | — |
| Icon button | `{ scale: 1.1 }` | `{ scale: 0.9 }` |
| Link | `{ x: 2 }` | — |
| Avatar | `{ scale: 1.05 }` | — |
**Anti-pattern:** Don't scale buttons more than 1.05 — it looks cartoonish. Subtle (1.01–1.03) feels premium.
---
## Pattern 6: Scroll-Triggered Animations
### Animate on Scroll Into View
```tsx
Appears when scrolled into view
```
**`viewport.once: true`** — Only animate the first time (most common for landing pages).
**`viewport.margin`** — Negative margin triggers earlier (before element is fully visible).
### Scroll-Linked Progress
```tsx
const { scrollYProgress } = useScroll()
const opacity = useTransform(scrollYProgress, [0, 0.3], [1, 0])
const y = useTransform(scrollYProgress, [0, 0.3], [0, -50])
Parallax hero content
```
---
## Pattern 7: Page Transitions
```tsx
// In your layout or page wrapper
{children}
```
**Keep it subtle.** Page transitions should be fast (200–300ms feel) and small (8–12px movement). Flashy page transitions feel like 2015.
---
## Pattern 8: Number/Text Transitions
```tsx
// Animate a counter
{count}
```
Wrap in `AnimatePresence` for the exit animation. Great for dashboards, pricing, live data.
---
## Anti-Patterns to Avoid
| ❌ Don't | ✅ Do Instead |
|----------|--------------|
| Animate `width`/`height` directly | Use `scale` or `layout` animations |
| Large movement values (`y: 200`) | Subtle values (`y: 16–24`) |
| Bounce on everything | Reserve bounce for playful/celebratory moments |
| Animate on every scroll event | Use `whileInView` with `once: true` |
| Different timing for every element | Use consistent spring configs project-wide |
| Animation on page load for everything | Prioritize above-the-fold; stagger the rest |
| Custom easing curves | Use springs — they respond to interruption better |
---
## Recommended Component Library References
When building animated components, reference these for patterns and inspiration:
- **[Magic UI](https://magicui.design)** — 150+ animated React components, shadcn/ui compatible
- **[Motion Primitives](https://motion-primitives.com)** — Copy-paste motion components for React
- **[Aceternity UI](https://ui.aceternity.com)** — Trendy animated components (heavier, more dramatic)
When a user wants a specific animated component (text reveal, animated border, gradient animation, etc.), check these libraries first — there's likely a battle-tested implementation.
---
## Quick Decision Guide
| Scenario | Pattern | Spring Config |
|----------|---------|--------------|
| Card appearing | Fade + Rise | smooth |
| List loading | Staggered List | smooth, 0.08s stagger |
| Tab switching | Shared Layout (`layoutId`) | snappy |
| Modal open/close | AnimatePresence + scale | smooth |
| Button press | whileHover + whileTap | snappy |
| Landing page sections | Scroll-triggered | gentle |
| Page navigation | Page Transition | smooth |
| Dashboard counter | Number Transition | snappy |
| Notification popup | Fade + Rise + bounce | bouncy |
| Accordion expand | Layout animation | smooth |
## Examples
### Example 1: "Add animations to this card grid"
Apply staggered fade+rise entrance to the grid, hover lift effect on each card:
```tsx
{cards.map((card) => (
))}
```
### Example 2: "Make this modal feel better"
Wrap in AnimatePresence, add scale + opacity entrance/exit, overlay fade:
```tsx
{isOpen && (
<>
>
)}
```
### Example 3: "Add scroll animations to this landing page"
Apply `whileInView` with staggered children to each section:
```tsx
Feature Section
{features.map((f) => (
))}
```