Framer Motion in Production: Animation Patterns That Don't Annoy Users
The five animation patterns I actually ship to production with Framer Motion — when to use spring vs tween, gesture-driven micro-interactions, scroll-triggered reveals, and what to avoid.
The line between delightful and annoying
Animations in production walk a thin line. Done well, they communicate state changes, guide attention, and make the product feel alive. Done badly, they slow users down and announce "this is a portfolio site, not a tool."
I've used Framer Motion on every React project for the last three years. Here are the patterns I reach for and the ones I now avoid.
Pattern 1: Spring for natural motion, tween for snap
The most common mistake: using tween (linear interpolation) for everything.
Physical objects move with momentum. A spring captures that — fast acceleration, slight overshoot, settle. It feels alive.
When to use tween: only when you need exact timing (synchronized animations, music-matched motion) or want a "snap" feel (e.g., closing a modal — sharp, definite).
Default: spring everywhere.
Pattern 2: Gesture-driven micro-interactions
The single highest-impact animation work: whileHover and whileTap.
Four lines: whileHover with scale 1.02, whileTap with scale 0.98. Real product polish. Users feel the button respond to their action.
Apply to: buttons, cards, links, anything interactive. Don't apply to: form inputs, content blocks, anything where movement would distract.
Pattern 3: Scroll-triggered reveal (sparingly)
Fade content in as it enters the viewport. Used everywhere on landing pages.
When to use: marketing or landing pages, blog post sections.
When to skip: app UIs where users will see the same content repeatedly. Reveals get old fast in tool UIs.
Pattern 4: Layout animations
Framer Motion's killer feature. When an element's position changes, animate it smoothly between positions.
Use cases:
The cost: layout animations are GPU-heavy. Don't apply to lists with over 50 items. For long lists, consider AnimatePresence with enter or exit instead.
Pattern 5: AnimatePresence for mount and unmount
For components that enter and leave the tree (modals, toasts, list items being removed), AnimatePresence lets you animate the exit.
Without AnimatePresence, exit animations don't play — the component just disappears. Critical for modals, drawers, dropdowns.
Patterns I now avoid
What I've stopped doing:
1. Page transitions on every route change
Tempting. Looks fancy. Adds perceived latency on every navigation. Users do hundreds of nav transitions per session. They notice.
If you must, use very fast (~150ms) crossfades, never slide or scale transitions.
2. Cursor effects (custom cursors, follower dots)
I built these on early portfolio versions. They look cool for 5 seconds, then they're noise. Removed.
3. Auto-playing hero videos with motion overlay
Heavy, distracting, kills LCP. A well-composed static image with subtle parallax does the same job at 1 tenth the cost.
4. Animation on text appearance
"Letters fade in one by one" is the AI-design-tool trademark of 2025. Don't.
5. Infinite scroll progress bars or loaders
If your loading takes more than 200ms, fix the loading, don't add a fancy loader.
Performance gotchas
Framer Motion is fast, but easy to misuse:
layout prop on hundreds of elements
Each layout element registers a measurement. 200 of them equals janky scroll. Fix: virtualize the list, or use layoutId only on the specific element that needs to fly between positions.
Animating width or height instead of scaleX or scaleY
Width and height triggers layout recalculation on every frame. Scale is GPU-only. Massive performance difference. For "the modal opens with a slide": animate scale or transform, not width or height.
Spring on properties that browsers can't easily animate
Color, background-color, border-radius — these need full repaints per frame. Use sparingly. Prefer fade or scale.
Forgetting will-change
For complex animations, add will-change transform style. Tells the browser to prepare a GPU layer.
Accessibility
Two things to honor:
prefers-reduced-motion
If users have set "reduce motion" in their OS, respect it. Framer Motion's useReducedMotion helper makes this trivial.
Don't animate focus states away
Buttons need visible focus rings for keyboard users. Don't put focus inside an animated container that hides the ring.
My production checklist
Before shipping any UI with animation:
TL;DR
If you have a React project where animation needs to feel premium without being annoying, contact me.
You might also like
Real-Time Apps with Next.js Server Actions and WebSockets in 2026
When Server Actions are enough, when you need a WebSocket layer, and how to wire Pusher / Soketi / Ably into a Next.js 14 App Router project without breaking SSR.
TypeScript Generics for React Engineers: A Practical Guide
The 6 generic patterns I use weekly on React + Next.js codebases — typed hooks, polymorphic components, discriminated unions, infer, constraints — without the academic noise.
React Server Components in Next.js 14: Production Patterns That Work
When to use Server Components vs Client Components in Next.js 14, the patterns that survive production, and the foot-guns I keep tripping over after shipping 8+ App Router projects.