React Example: Button Press with Spring Transition — Full Implementation Guide
Use Motion (formerly Framer Motion) with a spring configuration of { type: "spring", duration: 0.5, bounce: 0.2 } and animate scale from 1 to 0.97 on press for a natural, interruptible button interaction.
The Skills repository by Emil Kowalski is a curated collection of design-engineered patterns for building high-quality UI components. When implementing a React button press with spring transition, the repository provides a complete workflow—from library selection to exact spring values—ensuring your interactions feel alive and consistent with modern design standards.
Why Spring Physics Matter for Button Presses
The Skills system emphasizes spring-driven motion over traditional easing curves for interactive feedback. According to the repository's standards in skills/review-animations/STANDARDS.md, springs provide three critical advantages:
- Interruptibility — If a user releases the button mid-animation, the spring preserves momentum and finishes naturally rather than snapping to a keyframe endpoint
- Physicality — Simulated inertia makes the UI feel tactile and responsive, as documented in the Physicality section of
skills/review-animations/STANDARDS.md - Consistency — All interactive elements (modals, drawers, buttons) share the same spring configuration, creating a unified visual language
Recommended Animation Library
The repository's skills/pick-ui-library/SKILL.md explicitly recommends Motion (formerly Framer Motion) for spring-based UI interactions. Motion's useSpring hook provides velocity-preserving animations that align with the Skills design philosophy.
Key imports for this implementation:
import { motion, useSpring, useMotionValue } from "framer-motion";
The Canonical Spring Configuration
Per skills/review-animations/STANDARDS.md (lines 65–70), the Apple-style spring is the repository's default for press interactions:
const springConfig = { type: "spring", duration: 0.5, bounce: 0.2 };
This configuration balances responsiveness with polish—fast enough to feel snappy, bouncy enough to feel organic.
Complete React Button Implementation
Below is a production-ready component adapted from skills/emil-design-eng/SKILL.md that implements the Skills button press pattern:
import { motion, useSpring, useMotionValue } from "framer-motion";
export default function PressableButton({
children,
onClick,
}: {
children: React.ReactNode;
onClick?: () => void;
}) {
// Spring config from STANDARDS.md — Apple-style spring
const springConfig = { type: "spring", duration: 0.5, bounce: 0.2 };
const scale = useSpring(1, springConfig);
const isPressed = useMotionValue(false);
// 0.97 scale matches the "button-press feedback" standard
const handlePressStart = () => {
isPressed.set(true);
scale.set(0.97);
};
const handlePressEnd = () => {
isPressed.set(false);
scale.set(1);
};
return (
<motion.button
style={{ scale }}
onPointerDown={handlePressStart}
onPointerUp={handlePressEnd}
onPointerLeave={handlePressEnd}
onClick={onClick}
transition={springConfig}
className="px-4 py-2 bg-blue-600 text-white rounded"
>
{children}
</motion.button>
);
}
Implementation breakdown
| Step | Repository Reference | Purpose |
|---|---|---|
Import motion, useSpring |
skills/pick-ui-library/SKILL.md |
Use the recommended animation library |
Define springConfig object |
skills/review-animations/STANDARDS.md |
Apply the canonical Apple-style spring values |
Initialize scale with useSpring(1, springConfig) |
skills/emil-design-eng/SKILL.md |
Create a velocity-aware animated value |
Set scale.set(0.97) on press |
skills/emil-design-eng/SKILL.md |
Apply subtle press feedback (3% reduction) |
Use onPointerDown/Up/Leave |
skills/review-animations/STANDARDS.md |
Ensure interruptible, physically accurate motion |
Alternative: The CSS Fallback Pattern
The Skills repository also documents a progressive enhancement strategy in skills/emil-design-eng/SKILL.md. For environments without JavaScript or when Motion fails to load, apply a CSS-based press state:
button:active {
transform: scale(0.97);
transition: transform 0.2s ease-out;
}
This provides immediate feedback while the spring-enhanced version delivers the polished experience for capable browsers.
Key Repository Files
| File Path | Relevance to Button Press Implementation |
|---|---|
README.md |
Repository philosophy and skill index |
skills/pick-ui-library/SKILL.md |
Motion library recommendation |
skills/animate/SKILL.md |
Spring configuration theory and timing tables |
skills/review-animations/STANDARDS.md |
Exact spring values (duration: 0.5, bounce: 0.2) |
skills/emil-design-eng/SKILL.md |
Concrete useSpring examples and JSX patterns |
Summary
- Library choice: Motion (
useSpring,useMotionValue) perskills/pick-ui-library/SKILL.md - Spring config:
{ type: "spring", duration: 0.5, bounce: 0.2 }fromskills/review-animations/STANDARDS.md - Press transform:
scale(0.97)for subtle, consistent feedback - Implementation pattern:
onPointerDown/Uphandlers withscale.set()for full interruptibility - Fallback: CSS
:activestate for progressive enhancement
Frequently Asked Questions
What makes a spring transition better than a CSS transition for button presses?
A spring transition preserves velocity when interrupted. According to skills/review-animations/STANDARDS.md, if a user releases a button before the press animation completes, a spring continues from its current momentum rather than restarting or snapping, creating natural physical motion that CSS transitions cannot replicate.
Can I use whileTap instead of manual pointer handlers?
Yes—motion.button supports whileTap={{ scale: 0.97 }} with the same transition prop. However, the Skills repository's skills/emil-design-eng/SKILL.md demonstrates explicit handlers to illustrate spring configuration control and ensure readability for complex interactions where additional logic may be needed.
What if I need a different press scale than 0.97?
The 3% reduction (0.97) is the Skills standard for all press feedback. Deviating from this value breaks visual consistency with other interactive elements. If your design system requires adjustment, update skills/review-animations/STANDARDS.md as the single source of truth rather than hardcoding one-off values.
Does this pattern work with server components in Next.js App Router?
The component uses client-side hooks (useSpring, useMotionValue) and must be marked with the "use client" directive. The Skills repository's fallback CSS pattern ensures press feedback remains functional during server rendering or when JavaScript is disabled.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →