React-Spring vs Framer Motion: Comparing Examples in Two Animation Libraries

Framer Motion and react-spring both solve unique animation challenges and execute with high performance.

Framer Motion uses the more traditional duration-and-location approach. Most developers likely have experience with this animation strategy and it will feel familiar.

React-Spring approaches animations using spring-physics. This is a well-executed attempt to make animations feel and behave like real-life movement.

I was curious: how difficult is it to convert an animation from Framer Motion to react-spring? How about from react-spring to Framer Motion?

Below I will convert an example from the react-spring docs to Framer Motion, then convert an example from this Framer Motion article to react-spring.

GrowBall Example: Converting Framer Motion drag and useMotion to React-Spring useSpring

View the Code Sandbox

Framer Motion has a built-in suite of event APIs called Gestures. These APIs make handling certain user events easy and dynamic. In the Grow Ball example, the original Framer Motion version used drag to set drag constraints when dragging horizontally. Then useMotion and useTransition converted the x value to a scale for the ball SVG.

I expected the drag API would be difficult to replicate in react-spring. However, a third-party library called ‘react-with-gesture’, which is commonly used with react-spring, made the conversion painless.

const [bind, { delta, down }] = useGesture();
const { x } = useSpring({
  x: down ? delta[0] : 0
});
const transformations = interpolate(
  [
    x.interpolate({
      range: [-300, 300],
      output: [0.1, 1],
      extrapolate: "clamp"
    }),
    x
  ],
  (scale, x) => `translate3d(${x}px,0,0) scale(${scale})`
);

useGesture provides a way to track ‘down’ gestures and the change in x or y position. The hook is simply bound to a div, as shown below.

When down is triggered, useSpring captures the value and makes it available for use as an AnimatedValue. In this example, interpolate is called on x and it is transformed and outputs a value between 0.1 and 1, which is then used as the scale value. The original x value is also used to update the <animated.div /> x value.

return (
//skipping some boring code...
<animated.div
{...bind()}
style={{
...style,
transform: transformations
}}
/>
)

After plugging in the bind and transformations, we achieve parity with the original Framer Motion GrowBall animation.

Multistage Transitions: Converting React-Spring useTransition to Framer Motion Animate with Keyframes

The react-spring useTransition documentation includes this Codesandbox by Paul Henschel where an array of strings has multiple transitions applied successively for a slick and well-timed series of animations.

I recommend that you take a look at the react-spring codesandbox before taking a look at my below example in Framer Motion because, admittedly, I couldn’t match the exact timing and sequencing in my conversion.

My version after converting to Framer Motion: react-spring in Framer Motion

The react-spring useTransition hook has two significant features that I need to replicate. First, it monitors four states: fromenterleave, and update . Second, it easily handles multiple transitions per state. An example of this from the original react-spring Codesandbox is below:

enter: [
{ opacity: 1, height: 80, innerHeight: 80 },
{ transform: 'perspective(600px) rotateX(180deg)', color: '#28d79f' },
{ transform: 'perspective(600px) rotateX(0deg)' },
],

These are the dynamic kinds of functionality that I need to replicate in my conversion to Framer Motion.

After investigating the potential tools within Framer Motion, I settled on using <AnimatePresence /> and variants to handle the animations. <AnimatePresence /> allows for the animation of components that have left the DOM tree, which is needed for exit animation.

I encountered two significant challenges. First, the variant states in Framer Motion are limited to initialenter, and exit. The missing update means that animating updates (but not additions or subtractions) to our array values will have to be handled manually. Second, Framer Motion relies on keyframes to handle multi-stage transitions. This requires more syntax, more exact customization (such as setting animation times), and simply more knowledge of the underlying css. These are the exact things react-spring is designed to avoid by relying on lifelike spring animations instead of duration-and-location animations.

const variants = {
initial: { opacity: 0, height: 0, innerHeight: 0, color: '#8fa5b6' },
enter: {
opacity: [0, 0.5, 1],
height: 80,
innerHeight: 80,
color: ['#8fa5b6', '#28d79f'],
transform: [
'perspective(600px) rotateX(0deg) 1s',
'perspective(600px) rotateX(180deg)',
'perspective(600px) rotateX(0deg)',
],
transition: { color: { delay: 0.5 } },
},
exit: { color: '#c23369', innerHeight: 0, opacity: 0, height: 0, duration: 2 },
}

The above replicates most of the original react-spring animation. The timing isn’t the exact same, but more importantly, the update state is still missing. I might be able to compensate for this via more keyframes or perhaps through a more nuanced populating of the string array that hold the values getting animated.

<AnimatePresence>
  {items.map((item) => {
    return (
      <motion.div
        className="transitions-item"
        onClick={reset}
        key={item}
        variants={variants}
        initial="initial"
        animate="enter"
        exit="exit">
        <motion.div style={{ overflow: 'hidden', height: 80 }}>
          {item}
        </motion.div>
      </motion.div>
    )
  })}
</AnimatePresence>

The variants are simple to plug into the <AnimatePresence/> component.

Comparing React-Spring and Framer Motion

While there are nuances to each JavaScript library, there are also many areas where they overlap. Most notably:

  • Motions in Framer Motion are the corollary to Render-Props in react-spring — they have a different focus but each give highly specialized functionality to the libraries
  • Framer Motion useTransform is the corollary to react-spring interpolation
  • Framer Motion Gestures are the corollary to react-spring + react-with-gestures
  • Framer Motion drag is the corollary to react-spring clamp
  • Framer Motion animate is the corollary to react-spring useSpring

In fact, many of the examples in the react-spring docs are reminiscent of examples in the Framer Motion docs.

Naturally, there is a difference in the underlying philosophy of react-spring and Framer Motion. Each library was created to solve a different problem or provide certain features that the creators wanted.

I think Framer Motion will be best when I want to create animations quickly, because duration-and-location animation is what I have prior experience with. I would tend towards react-spring if I want spring physics look-and-feel in my project. Also, I would consider Framer Motion Gestures and react-spring + react-with-gesture and consider if either out-of-the-box animation suite had the exact event I needed.

When choosing a library for a project, consider the end goal of the project, the look and feel that you want your animations to have, and don’t be afraid to try something new.

Share this post:

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.