Framer Motion in Production: Animation Patterns That Don't Annoy Users
ReactFrontend

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.

HJ
Hassan Javed
January 2026
9 min read

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:

Filtering a list — items animate to their new positions
Expanding a card — content reflows smoothly
Active state on a tab — the underline slides between tabs

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:

Tested with system "reduce motion" on — animations should still work, just instant
Spring on motion, tween only where intentional
No layout on lists over 50 items
No animation over 500ms duration on app UI (300ms max ideally)
All buttons have whileTap feedback
Modals or drawers use AnimatePresence with exit animations
No page transitions on internal navigation

TL;DR

Spring physics for natural motion, tween only when you need precision or snap
whileHover plus whileTap on every interactive element — biggest impact for least work
Scroll-reveal on marketing pages, not app UIs
layout for filtering or reordering, AnimatePresence for mount or unmount
Avoid page transitions, custom cursors, text-letter animations
Respect reduce-motion preference

If you have a React project where animation needs to feel premium without being annoying, contact me.

Related Reads

You might also like