react-spring-physics

Physics-based animation library combining React Spring (spring dynamics, gesture integration, 60fps animations) and Popmotion (low-level composable animation…

INSTALLATION
npx skills add https://github.com/freshtechbro/claudedesignskills --skill react-spring-physics
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$28

Core Concepts

Spring Physics

Springs animate values from current state to target state using physical simulation:

import { useSpring, animated } from '@react-spring/web'

function SpringExample() {

  const springs = useSpring({

    from: { opacity: 0, y: -40 },

    to: { opacity: 1, y: 0 },

    config: {

      mass: 1,        // Weight of object

      tension: 170,   // Spring strength

      friction: 26    // Opposing force

    }

  })

  return <animated.div style={springs}>Hello</animated.div>

}

useSpring Hook Patterns

Two initialization patterns for different use cases:

// Object config (simpler, auto-updates on prop changes)

const springs = useSpring({

  from: { x: 0 },

  to: { x: 100 }

})

// Function config (more control, returns API for imperative updates)

const [springs, api] = useSpring(() => ({

  from: { x: 0 }

}), [])

// Trigger animation via API

const handleClick = () => {

  api.start({

    from: { x: 0 },

    to: { x: 100 }

  })

}

Spring Configuration Presets

React Spring provides built-in config presets:

import { config } from '@react-spring/web'

// Available presets

config.default  // { tension: 170, friction: 26 }

config.gentle   // { tension: 120, friction: 14 }

config.wobbly   // { tension: 180, friction: 12 }

config.stiff    // { tension: 210, friction: 20 }

config.slow     // { tension: 280, friction: 60 }

config.molasses // { tension: 280, friction: 120 }

// Usage

const springs = useSpring({

  from: { x: 0 },

  to: { x: 100 },

  config: config.wobbly

})

Common Patterns

1. Click-Triggered Spring Animation

import { useSpring, animated } from '@react-spring/web'

function ClickAnimated() {

  const [springs, api] = useSpring(() => ({

    from: { scale: 1 }

  }), [])

  const handleClick = () => {

    api.start({

      from: { scale: 1 },

      to: { scale: 1.2 },

      config: { tension: 300, friction: 10 }

    })

  }

  return (

    <animated.button

      onClick={handleClick}

      style={{

        transform: springs.scale.to(s => `scale(${s})`)

      }}

    >

      Click Me

    </animated.button>

  )

}

2. Multi-Element Trail Animation

import { useTrail, animated } from '@react-spring/web'

function Trail({ items }) {

  const trails = useTrail(items.length, {

    from: { opacity: 0, x: -20 },

    to: { opacity: 1, x: 0 },

    config: config.gentle

  })

  return (

    <div>

      {trails.map((style, i) => (

        <animated.div key={i} style={style}>

          {items[i]}

        </animated.div>

      ))}

    </div>

  )

}

3. List Transitions (Enter/Exit)

import { useTransition, animated } from '@react-spring/web'

function List({ items }) {

  const transitions = useTransition(items, {

    from: { opacity: 0, height: 0 },

    enter: { opacity: 1, height: 80 },

    leave: { opacity: 0, height: 0 },

    config: config.stiff,

    keys: item => item.id

  })

  return transitions((style, item) => (

    <animated.div style={style}>

      {item.text}

    </animated.div>

  ))

}

4. Scroll-Based Spring Animation

import { useScroll, animated } from '@react-spring/web'

function ScrollReveal() {

  const { scrollYProgress } = useScroll()

  return (

    <animated.div

      style={{

        opacity: scrollYProgress.to([0, 0.5], [0, 1]),

        scale: scrollYProgress.to([0, 0.5], [0.8, 1])

      }}

    >

      Scroll to reveal

    </animated.div>

  )

}

5. Viewport Intersection Animation

import { useInView, animated } from '@react-spring/web'

function FadeInOnView() {

  const [ref, springs] = useInView(

    () => ({

      from: { opacity: 0, y: 100 },

      to: { opacity: 1, y: 0 }

    }),

    { rootMargin: '-40% 0%' }

  )

  return <animated.div ref={ref} style={springs}>Content</animated.div>

}

6. Chained Async Animations

import { useSpring, animated } from '@react-spring/web'

function ChainedAnimation() {

  const springs = useSpring({

    from: { x: 0, background: '#ff6d6d' },

    to: [

      { x: 80, background: '#fff59a' },

      { x: 0, background: '#88DFAB' },

      { x: 80, background: '#569AFF' }

    ],

    config: { tension: 200, friction: 20 },

    loop: true

  })

  return <animated.div style={springs} />

}

7. Spring with Velocity Preservation

import { useSpring, animated } from '@react-spring/web'

function VelocityPreservation() {

  const [springs, api] = useSpring(() => ({

    x: 0,

    config: { tension: 300, friction: 30 }

  }), [])

  const handleDragEnd = () => {

    api.start({

      x: 0,

      velocity: springs.x.getVelocity(), // Preserve momentum

      config: { tension: 200, friction: 20 }

    })

  }

  return <animated.div style={springs} onMouseUp={handleDragEnd} />

}

Integration Patterns

With React Three Fiber (3D)

import { useSpring, animated } from '@react-spring/three'

import { Canvas } from '@react-three/fiber'

const AnimatedBox = animated(MeshDistortMaterial)

function ThreeScene() {

  const [clicked, setClicked] = useState(false)

  const springs = useSpring({

    scale: clicked ? 1.5 : 1,

    color: clicked ? '#569AFF' : '#ff6d6d',

    config: { tension: 200, friction: 20 }

  })

  return (

    <Canvas>

      <mesh onClick={() => setClicked(!clicked)} scale={springs.scale}>

        <sphereGeometry args={[1, 64, 32]} />

        <AnimatedBox color={springs.color} />

      </mesh>

    </Canvas>

  )

}

With Popmotion (Low-Level Physics)

import { spring, inertia } from 'popmotion'

import { useState } from 'react'

function PopmotionIntegration() {

  const [x, setX] = useState(0)

  const handleDragEnd = (velocity) => {

    inertia({

      from: x,

      velocity: velocity,

      power: 0.3,

      timeConstant: 400,

      modifyTarget: v => Math.round(v / 100) * 100 // Snap to grid

    }).start(setX)

  }

  return <div style={{ transform: `translateX(${x}px)` }} />

}

With Forms and Validation

import { useSpring, animated } from '@react-spring/web'

function ValidatedInput() {

  const [error, setError] = useState(false)

  const shakeAnimation = useSpring({

    x: error ? [0, -10, 10, -10, 10, 0] : 0,

    config: { tension: 300, friction: 10 },

    onRest: () => setError(false)

  })

  return <animated.input style={shakeAnimation} />

}

Performance Optimization

On-Demand Rendering

// Only re-render when animation is active

const [springs, api] = useSpring(() => ({

  from: { x: 0 },

  config: { precision: 0.01 } // Higher value = less updates

}), [])

Batch Multiple Springs

// Use useSprings for multiple similar animations

const springs = useSprings(

  items.length,

  items.map(item => ({

    from: { opacity: 0 },

    to: { opacity: 1 }

  }))

)

Skip Animation (Testing/Accessibility)

import { Globals } from '@react-spring/web'

// Skip all animations (prefers-reduced-motion)

useEffect(() => {

  Globals.assign({ skipAnimation: true })

  return () => Globals.assign({ skipAnimation: false })

}, [])

Common Pitfalls

1. Forgetting Dependencies Array

// ❌ Wrong: No dependencies, creates new spring every render

const springs = useSpring(() => ({ x: 0 }))

// ✅ Correct: Empty array prevents recreation

const [springs, api] = useSpring(() => ({ x: 0 }), [])

2. Mutating Spring Values

// ❌ Wrong: Direct mutation

springs.x.set(100)

// ✅ Correct: Use API to animate

api.start({ x: 100 })

3. Ignoring Config Precision

// ❌ Default precision too fine (0.0001), causing unnecessary renders

const springs = useSpring({ x: 0 })

// ✅ Set appropriate precision for your use case

const springs = useSpring({

  x: 0,

  config: { precision: 0.01 } // Stop updating when within 0.01 of target

})

4. Not Handling Velocity

// ❌ Abrupt stop when interrupting animation

api.start({ x: 0 })

// ✅ Preserve momentum

api.start({

  x: 0,

  velocity: springs.x.getVelocity()

})

5. Mixing Config Patterns

// ❌ Wrong: Using both object and function config

const springs = useSpring({

  from: { x: 0 }

})

api.start({ x: 100 }) // api is undefined

// ✅ Correct: Use function config for imperative control

const [springs, api] = useSpring(() => ({

  from: { x: 0 }

}), [])

6. Animating Non-Numerical Values

// ❌ Wrong: Spring can't interpolate complex strings directly

const springs = useSpring({ transform: 'translateX(100px) rotate(45deg)' })

// ✅ Correct: Animate individual values

const springs = useSpring({ x: 100, rotation: 45 })

// Then combine: transform: `translateX(${x}px) rotate(${rotation}deg)`

Resources

Scripts

  • spring_generator.py - Generate React Spring boilerplate code
  • physics_calculator.py - Calculate optimal spring physics parameters

References

  • react_spring_api.md - Complete React Spring hooks and API reference
  • popmotion_api.md - Popmotion functions and reactive streams
  • physics_guide.md - Spring physics deep dive with tuning guide

Assets

  • starter_spring/ - React + Vite template with React Spring examples
  • examples/ - Real-world patterns (gestures, scroll, 3D integration)

Related Skills

  • motion-framer - Alternative declarative animation approach with variants
  • gsap-scrolltrigger - Timeline-based animations for complex sequences
  • react-three-fiber - 3D scene management (use @react-spring/three for animations)
  • animated-component-libraries - Pre-built animated components using Motion

Physics vs Timeline: Use React Spring for natural, physics-based motion that responds to user input. Use GSAP for precise, timeline-based choreography and complex multi-step sequences.

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card