withSpring
The withSpring modifier creates natural, physics-based animations using spring physics. It's perfect for UI elements that should feel alive and responsive, like buttons, cards, modals, and interactive components.
Why Use withSpring?
Spring animations feel natural because they simulate real-world physics:
- Bounce - Natural overshoot and settle
- Responsive - Adapts to different screen sizes and devices
- Smooth - No jarring stops or starts
- Performant - Optimized for 60fps animations
Basic Syntax
withSpring(toValue, options?)
Parameters
- toValue (required): The target value. Can be a
number,string,object, orarray. - options (optional): Configuration object for fine-tuning the spring behavior.
Simple Example
The simplest spring animation:
import { useValue, withSpring, animate } from 'react-ui-animate';
function MyComponent() {
const [width, setWidth] = useValue(100);
return (
<>
<button onClick={() => setWidth(withSpring(200))}>
Expand
</button>
<animate.div style={{ width, height: 100, background: 'teal' }} />
</>
);
}
Configuration Options
Fine-tune your spring animations with these options:
| Option | Type | Default | Description |
|---|---|---|---|
stiffness | number | 158 | How "stiff" the spring is. Higher = faster, more bouncy |
damping | number | 20 | How quickly the spring loses energy. Higher = less bouncy, more controlled |
mass | number | 1 | The "weight" of the spring. Higher = slower, more inertia |
onStart | () => void | - | Called when animation starts |
onChange | (value) => void | - | Called on every frame with current value |
onComplete | () => void | - | Called when animation finishes |
Understanding Spring Parameters
Stiffness - Controls speed and bounce
- Low (50-100): Slow, gentle motion
- Medium (150-200): Balanced, natural feel (default)
- High (300+): Fast, snappy motion
Damping - Controls bounce and overshoot
- Low (5-15): Very bouncy, lots of overshoot
- Medium (20-30): Balanced bounce (default)
- High (40+): Minimal bounce, controlled motion
Mass - Controls inertia
- Low (0.5-1): Light, quick response (default)
- Medium (1-2): Balanced
- High (3+): Heavy, slow to start/stop
Real-World Examples
Example 1: Button Hover Effect
Create a responsive button that springs on hover:
function SpringButton() {
const [scale, setScale] = useValue(1);
return (
<animate.button
onMouseEnter={() => setScale(withSpring(1.1, { stiffness: 300 }))}
onMouseLeave={() => setScale(withSpring(1))}
style={{
scale,
padding: '10px 20px',
background: 'teal',
border: 'none',
borderRadius: 4,
}}
>
Hover me
</animate.button>
);
}
Example 2: Modal Animation
Smooth modal entrance with spring:
function Modal({ isOpen, onClose }) {
const [scale, setScale] = useValue(0);
useEffect(() => {
if (isOpen) {
setScale(withSpring(1, { stiffness: 200, damping: 25 }));
} else {
setScale(withSpring(0));
}
}, [isOpen]);
return (
<div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)' }}>
<animate.div
style={{
scale,
opacity: scale,
background: 'white',
padding: 20,
borderRadius: 8,
}}
>
Modal Content
<button onClick={onClose}>Close</button>
</animate.div>
</div>
);
}
Example 3: Card Flip Animation
Animate multiple properties together:
function FlipCard() {
const [rotation, setRotation] = useValue(0);
const [scale, setScale] = useValue(1);
const flip = () => {
setRotation(withSpring(rotation + 180));
setScale(withSpring(0.95, { stiffness: 300 }));
setTimeout(() => {
setScale(withSpring(1));
}, 200);
};
return (
<animate.div
onClick={flip}
style={{
rotate: rotation,
scale,
width: 200,
height: 200,
background: 'teal',
borderRadius: 8,
}}
>
Click to flip
</animate.div>
);
}
Animating Multiple Values
Spring works great with objects and arrays:
const [position, setPosition] = useValue({ x: 0, y: 0 });
// Animate both x and y together
setPosition(withSpring({ x: 100, y: 100 }));
Using Callbacks
React to animation lifecycle events:
function AnimatedCard() {
const [x, setX] = useValue(0);
const animate = () => {
setX(withSpring(200, {
onStart: () => {
console.log('Animation started');
// Disable button, show loading, etc.
},
onChange: (value) => {
// Track progress, update other values
console.log('Current position:', value);
},
onComplete: () => {
console.log('Animation finished');
// Re-enable button, hide loading, etc.
},
}));
};
return (
<>
<button onClick={animate}>Animate</button>
<animate.div style={{ translateX: x, width: 100, height: 100, background: 'teal' }} />
</>
);
}
Preset Configurations
Common spring configurations for different use cases:
// Gentle, smooth animation
const gentle = { stiffness: 120, damping: 25 };
// Snappy, quick animation
const snappy = { stiffness: 300, damping: 30 };
// Bouncy, playful animation
const bouncy = { stiffness: 200, damping: 10 };
// Controlled, minimal bounce
const controlled = { stiffness: 180, damping: 35 };
// Usage
setX(withSpring(100, gentle));
Best Practices
✅ Do
- Use
withSpringfor most UI animations - it feels natural - Adjust
stiffnessanddampingto match your design language - Use objects/arrays for coordinated multi-property animations
- Leverage callbacks for side effects and state management
❌ Don't
- Don't use
withSpringwhen you need exact timing (usewithTiminginstead) - Don't set extremely high stiffness values (>500) - can cause jank
- Don't forget to handle the animation lifecycle with callbacks when needed
- Don't animate layout properties (width, height) - use transforms instead
Performance Tips
- Spring is optimized - It's one of the fastest modifiers
- Use transforms -
translateX,scale,rotateare GPU-accelerated - Batch updates - Animate multiple properties in one object
- Avoid layout properties -
width,height,top,leftcause reflows
Common Patterns
Pattern 1: Quick Bounce
const [scale, setScale] = useValue(1);
const bounce = () => {
setScale(withSpring(1.2, { stiffness: 400, damping: 15 }));
setTimeout(() => setScale(withSpring(1)), 150);
};
Pattern 2: Smooth Entrance
const [opacity, setOpacity] = useValue(0);
const [translateY, setTranslateY] = useValue(-20);
useEffect(() => {
setOpacity(withSpring(1, { stiffness: 200, damping: 25 }));
setTranslateY(withSpring(0));
}, []);
Pattern 3: Interactive Feedback
const [rotation, setRotation] = useValue(0);
const handleClick = () => {
setRotation(withSpring(rotation + 360, { stiffness: 300 }));
};
Troubleshooting
Animation feels too slow:
- Increase
stiffness(try 200-300) - Decrease
dampingslightly
Animation is too bouncy:
- Increase
damping(try 25-35) - Decrease
stiffnessslightly
Animation doesn't feel smooth:
- Make sure you're using
animate.div(or otheranimate.*components) - Check that you're not causing re-renders during animation
- Use transforms instead of layout properties
Next Steps
- Learn about withTiming for precise time-based animations
- Explore withSequence to chain spring animations
- Check out useValue for more animation patterns