Skip to main content

React Spring Animation Guide

Framer Motion simplifies spring animations by providing a declarative API with pre-configured spring profiles. Instead of calculating velocity, damping, and stiffness manually, you define a transition object with properties like type: "spring", stiffness, and damping. This library, widely used in 2026, integrates seamlessly with React components and gesture handlers, making it the standard for physics-based motion.

Introduction to Framer Motion Spring Animations

Framer Motion's spring animations move a value from its current state to a target state with natural acceleration and deceleration. Here's a basic spring animation:

import React, { useState } from 'react';
import { motion } from 'framer-motion';

export default function BasicSpringAnimation() {
const [isOpen, setIsOpen] = useState(false);

return (
<div style={{ padding: '40px' }}>
<button
onClick={() => setIsOpen(!isOpen)}
style={{ padding: '8px 16px', cursor: 'pointer', marginBottom: '20px' }}
>
Toggle Box
</button>

{isOpen && (
<motion.div
initial={{ opacity: 0, scale: 0.5 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.5 }}
transition={{ type: 'spring', stiffness: 100, damping: 10 }}
style={{
width: '200px',
height: '200px',
backgroundColor: '#3b82f6',
borderRadius: '8px',
}}
/>
)}
</div>
);
}

The transition prop specifies type: "spring" and tuning parameters. stiffness controls how fast the spring pulls (higher = faster), and damping controls oscillation (higher = less bouncy).

Spring Presets for Common Interactions

Framer Motion provides preset spring configs for different interaction types. Use layout, smooth, slow, stiff, or wobbly for quick prototyping:

import React from 'react';
import { motion } from 'framer-motion';

export default function SpringPresets() {
const presets = [
{ name: 'Gentle', config: { type: 'spring', damping: 25, stiffness: 100 } },
{ name: 'Snappy', config: { type: 'spring', damping: 15, stiffness: 200 } },
{ name: 'Bouncy', config: { type: 'spring', damping: 8, stiffness: 200 } },
{ name: 'Elastic', config: { type: 'spring', damping: 5, stiffness: 150 } },
];

return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '20px', padding: '20px' }}>
{presets.map((preset) => (
<motion.div
key={preset.name}
initial={{ y: -50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={preset.config}
style={{
padding: '20px',
backgroundColor: '#f0f9ff',
borderRadius: '8px',
border: '2px solid #0ea5e9',
}}
>
<h3 style={{ margin: '0 0 8px 0' }}>{preset.name}</h3>
<p style={{ margin: 0, fontSize: '12px', color: '#64748b' }}>
Damping: {preset.config.damping}, Stiffness: {preset.config.stiffness}
</p>
</motion.div>
))}
</div>
);
}

Experiment with these presets to find the feel that matches your design. Gentle is suitable for subtle UI updates, Snappy for quick confirmations, Bouncy for playful interactions, and Elastic for dramatic effects.

Dragging with Spring Return Animation

Combine Framer Motion's drag handler with spring animations to create a drag-and-release pattern that smoothly returns to the origin:

import React from 'react';
import { motion } from 'framer-motion';

export default function DragWithSpringReturn() {
return (
<div
style={{
width: '100%',
height: '400px',
border: '2px solid #ccc',
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f9f9f9',
}}
>
<motion.div
drag
dragElastic={0.2}
onDragEnd={(event, info) => {
console.log('Velocity:', info.velocity);
}}
style={{
width: '100px',
height: '100px',
backgroundColor: '#ec4899',
borderRadius: '8px',
cursor: 'grab',
}}
whileDrag={{ cursor: 'grabbing' }}
transition={{
type: 'spring',
damping: 15,
stiffness: 200,
restDelta: 0.001,
}}
>
Drag me
</motion.div>
</div>
);
}

The dragElastic property (0.2) creates a stretchy feel while dragging. When released, the spring animation with stiffness: 200 and damping: 15 snaps back to the origin quickly.

Gesture-Driven Spring Animations

Combine Framer Motion with framer-motion's useMotionValue and useTransform for gesture-driven animations that feel responsive:

import React from 'react';
import { motion, useMotionValue, useTransform } from 'framer-motion';

export default function GestureSpringAnimation() {
const x = useMotionValue(0);
const y = useMotionValue(0);
const rotateZ = useTransform(
[x, y],
([latestX, latestY]) =>
Math.atan2(latestY, latestX) * (180 / Math.PI)
);

return (
<div
style={{
width: '100%',
height: '400px',
border: '2px solid #ccc',
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f9f9f9',
}}
>
<motion.div
drag
dragElastic={0.1}
x={x}
y={y}
rotateZ={rotateZ}
style={{
width: '80px',
height: '80px',
backgroundColor: '#8b5cf6',
borderRadius: '8px',
cursor: 'grab',
}}
whileDrag={{ scale: 1.1 }}
transition={{
type: 'spring',
damping: 10,
stiffness: 150,
}}
>
Drag & Rotate
</motion.div>
</div>
);
}

This example transforms drag position into rotation angle, creating an interactive element that spins based on how far you drag it. The spring animation smoothly interpolates the rotation.

Key Takeaways

  • Framer Motion's spring transitions abstract physics calculations; use type: "spring" with stiffness and damping to tune motion feel.
  • Higher stiffness (150+) creates snappy, responsive motion; lower stiffness (70–100) creates gentle, slow animations.
  • Higher damping (20+) reduces oscillation and settling time; lower damping (5–15) allows bouncy, playful motion.
  • Combine drag handlers with spring transitions to create natural, responsive gesture interactions that return to origin on release.

Frequently Asked Questions

What's the difference between stiffness and mass in Framer Motion?

Stiffness controls how hard the spring pulls (higher = faster approach to target). Mass affects the momentum during oscillation (higher mass = more bouncing). Damping controls how quickly the bounce decays. For most UI animations, stiffness and damping are sufficient.

How do I prevent a spring animation from overshooting too much?

Increase damping. For example, change damping from 10 to 20 or 25 to reduce overshoot. Alternatively, reduce stiffness (from 200 to 150) to make the approach slower and less prone to overshoot.

Can I animate multiple properties with different spring configs?

Yes, create a transition object with named transitions: transition={{ x: { type: "spring", stiffness: 100 }, rotate: { type: "spring", stiffness: 50 } }}. This lets each property have its own physics.

How do I make a spring animation loop?

Use animate={{ y: [0, 20, 0] }} with transition={{ type: "spring", repeat: Infinity }}. The animation will cycle through the y values infinitely. Set repeatDelay to add pauses between cycles.

Should I use Framer Motion or build custom spring physics?

For production apps, Framer Motion is mature, well-tested, and handles edge cases. Custom physics is lightweight and educational for learning, but error-prone in complex scenarios. Use Framer Motion for shipping products.

Further Reading